Showing preview only (450K chars total). Download the full file or copy to clipboard to get everything.
Repository: Arvamer/gilrs
Branch: master
Commit: 78581b7d5842
Files: 64
Total size: 427.7 KB
Directory structure:
gitextract_3om1ehps/
├── .gitignore
├── .gitlab-ci.yml
├── .gitmodules
├── Cargo.toml
├── LICENSE-APACHE
├── LICENSE-MIT
├── README.md
├── appveyor.yml
├── gilrs/
│ ├── CHANGELOG.md
│ ├── Cargo.toml
│ ├── build.rs
│ ├── examples/
│ │ ├── ev.rs
│ │ ├── ff.rs
│ │ ├── ff_pos.rs
│ │ ├── gamepad_info.rs
│ │ ├── gui.rs
│ │ └── wasm/
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── index.html
│ │ ├── wasm_gui.ps1
│ │ └── wasm_gui.sh
│ └── src/
│ ├── constants.rs
│ ├── ev/
│ │ ├── filter.rs
│ │ ├── mod.rs
│ │ └── state.rs
│ ├── ff/
│ │ ├── base_effect.rs
│ │ ├── effect_source.rs
│ │ ├── mod.rs
│ │ ├── server.rs
│ │ └── time.rs
│ ├── gamepad.rs
│ ├── lib.rs
│ ├── mapping/
│ │ ├── mod.rs
│ │ └── parser.rs
│ └── utils.rs
├── gilrs-core/
│ ├── CHANGELOG.md
│ ├── Cargo.toml
│ ├── README.md
│ ├── examples/
│ │ └── ev_core.rs
│ └── src/
│ ├── lib.rs
│ ├── platform/
│ │ ├── default/
│ │ │ ├── ff.rs
│ │ │ ├── gamepad.rs
│ │ │ └── mod.rs
│ │ ├── linux/
│ │ │ ├── ff.rs
│ │ │ ├── gamepad.rs
│ │ │ ├── ioctl.rs
│ │ │ ├── mod.rs
│ │ │ └── udev.rs
│ │ ├── macos/
│ │ │ ├── ff.rs
│ │ │ ├── gamepad.rs
│ │ │ ├── io_kit.rs
│ │ │ └── mod.rs
│ │ ├── mod.rs
│ │ ├── wasm/
│ │ │ ├── ff.rs
│ │ │ ├── gamepad.rs
│ │ │ └── mod.rs
│ │ ├── windows_wgi/
│ │ │ ├── ff.rs
│ │ │ ├── gamepad.rs
│ │ │ └── mod.rs
│ │ └── windows_xinput/
│ │ ├── ff.rs
│ │ ├── gamepad.rs
│ │ └── mod.rs
│ └── utils.rs
└── rustfmt.toml
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
target
Cargo.lock
!.gitignore
================================================
FILE: .gitlab-ci.yml
================================================
image: "rust:1.80"
variables:
CARGO_HOME: $CI_PROJECT_DIR/cargo
GIT_SUBMODULE_STRATEGY: normal
prepare:lockfile:
stage: .pre
image: "rust:1"
script:
- CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS=fallback cargo generate-lockfile
artifacts:
paths:
- Cargo.lock
test:x86_64-unknown-linux-gnu:
stage: test
before_script:
- apt-get update -yqq && apt-get install -yqq libudev-dev
# eframe dependencies for testing gui example
- apt-get install -yqq libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libspeechd-dev libxkbcommon-dev libssl-dev
- rustc -Vv && cargo -Vv
script:
- cargo test --verbose --all --features serde-serialize
test:i686-unknown-linux-gnu:
stage: test
variables:
PKG_CONFIG_ALLOW_CROSS: "1"
before_script:
- dpkg --add-architecture i386
- apt-get update -yqq && apt-get install -yqq gcc-multilib libudev-dev libudev1:i386 libudev-dev:i386
- rustc -Vv && cargo -Vv
- rustup target add i686-unknown-linux-gnu
script:
- cargo test --verbose --target=i686-unknown-linux-gnu --lib --features serde-serialize
build:wasm32-unknown-unknown:
image: "rust:1.80"
stage: test
before_script:
- rustc -Vv && cargo -Vv
- rustup target add wasm32-unknown-unknown
script:
- cargo test --no-run --target wasm32-unknown-unknown --all --features serde-serialize
check:x86_64-apple-darwin:
stage: test
before_script:
- rustc -Vv && cargo -Vv
- rustup target add x86_64-apple-darwin
script:
- cargo check --target=x86_64-apple-darwin --verbose --all --features serde-serialize
================================================
FILE: .gitmodules
================================================
[submodule "SDL_GameControllerDB"]
path = gilrs/SDL_GameControllerDB
url = https://github.com/gabomdq/SDL_GameControllerDB.git
================================================
FILE: Cargo.toml
================================================
[workspace]
resolver = "2"
members = [
"gilrs"
]
================================================
FILE: LICENSE-APACHE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: LICENSE-MIT
================================================
Copyright (c) 2016-2018 Mateusz Sieczko and other GilRs
Developers
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================
GilRs - Game Input Library for Rust
===================================
[](https://gitlab.com/gilrs-project/gilrs/commits/master)
[](https://crates.io/crates/gilrs)
[](https://docs.rs/gilrs/)
[](https://gitlab.com/gilrs-project/gilrs)
GilRs abstract platform specific APIs to provide unified interfaces for working with gamepads.
Main features:
- Unified gamepad layout—buttons and axes are represented by familiar names
- Support for SDL2 mappings including `SDL_GAMECONTROLLERCONFIG` environment
variable which Steam uses
- Hotplugging—GilRs will try to assign new IDs for new gamepads and reuse the same
ID for gamepads which reconnected
- Force feedback (rumble)
- Power information (is gamepad wired, current battery status)
The project's main repository [is on GitLab](https://gitlab.com/gilrs-project/gilrs)
although there is also a [GitHub mirror](https://github.com/Arvamer/gilrs).
Please use GitLab's issue tracker and merge requests.
This repository contains submodule; after you clone it, don't forget to run
`git submodule init; git submodule update` (or clone with `--recursive` flag)
or you will get compile errors.
Example
-------
```toml
[dependencies]
gilrs = "0.11.0"
```
```rust
use gilrs::{Gilrs, Button, Event};
let mut gilrs = Gilrs::new().unwrap();
// Iterate over all connected gamepads
for (_id, gamepad) in gilrs.gamepads() {
println!("{} is {:?}", gamepad.name(), gamepad.power_info());
}
let mut active_gamepad = None;
loop {
// Examine new events
while let Some(Event { id, event, time, .. }) = gilrs.next_event() {
println!("{:?} New event from {}: {:?}", time, id, event);
active_gamepad = Some(id);
}
// You can also use cached gamepad state
if let Some(gamepad) = active_gamepad.map(|id| gilrs.gamepad(id)) {
if gamepad.is_pressed(Button::South) {
println!("Button South is pressed (XBox - A, PS - X)");
}
}
}
```
Supported features
------------------
| | Input | Hotplugging | Force feedback |
|------------------|:-----:|:-----------:|:--------------:|
| Linux/BSD (evdev)| ✓ | ✓ | ✓ |
| Windows | ✓ | ✓ | ✓ |
| OS X | ✓ | ✓ | ✕ |
| Wasm | ✓ | ✓ | n/a |
| Android | ✕ | ✕ | ✕ |
Platform specific notes
======================
Linux/BSD (evdev)
-----
With evdev, GilRs read (and write, in case of force feedback) directly from appropriate
`/dev/input/event*` file. This mean that user have to have read and write access to this file.
On most distros it shouldn't be a problem, but if it is, you will have to create udev rule.
On FreeBSD generic HID gamepads use hgame(4) and special use Linux driver via `webcamd`.
To build GilRs, you will need pkg-config and libudev .pc file. On some distributions this file
is packaged in separate archive (e.g., `libudev-dev` in Debian, `libudev-devd` in FreeBSD).
Windows
-----
Windows defaults to using Windows Gaming Input instead of XInput. If you need to use XInput you
can disable the `wgi` feature (it's enabled by default) and enable the `xinput` feature.
Windows Gaming Input requires an in focus window to be associated with the process to receive
events. You can still switch back to using xInput by turning off default features and enabling
the xinput feature.
Note: Some (Older?) devices may still report inputs without a window but this is not the case
for all devices so if you are writing a terminal based game, use the xinput feature instead.
Wasm
-----
Wasm implementation uses stdweb, or wasm-bindgen with the wasm-bindgen feature.
For stdweb, you will need [cargo-web](https://github.com/koute/cargo-web) to build gilrs for
wasm32-unknown-unknown. For wasm-bindgen, you will need the wasm-bindgen cli or a tool like
[wasm-pack](https://rustwasm.github.io/wasm-pack/installer/).
Unlike other platforms, events are only generated when you call `Gilrs::next_event()`.
See [`./gilrs/examples/wasm/README.md`](./gilrs/examples/wasm/README.md) for running the examples using Wasm.
License
=======
This project is licensed under the terms of both the Apache License (Version 2.0) and the MIT
license. See LICENSE-APACHE and LICENSE-MIT for details.
================================================
FILE: appveyor.yml
================================================
# Based on the "trust" template v0.1.1
# https://github.com/japaric/trust/tree/v0.1.1
image:
- Visual Studio 2022
environment:
global:
RUST_VERSION: 1.80.0
CRATE_NAME: gilrs
matrix:
- TARGET: x86_64-pc-windows-msvc
install:
- ps: >-
If ($Env:TARGET -eq 'x86_64-pc-windows-gnu') {
$Env:PATH += ';C:\msys64\mingw64\bin'
} ElseIf ($Env:TARGET -eq 'i686-pc-windows-gnu') {
$Env:PATH += ';C:\msys64\mingw32\bin'
}
- curl -sSf -o rustup-init.exe https://win.rustup.rs/
- rustup-init.exe -y --default-host %TARGET% --default-toolchain %RUST_VERSION%
- set PATH=%PATH%;C:\Users\appveyor\.cargo\bin
- rustup toolchain install stable
- rustc -Vv
- cargo -V
- git submodule update --init --recursive
- set CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS=fallback
- cargo +stable generate-lockfile
- set CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS=
test_script:
- cargo test --verbose --target %TARGET%
cache:
- C:\Users\appveyor\.cargo\registry
- target
# Building is done in the test phase, so we disable Appveyor's build phase.
build: false
================================================
FILE: gilrs/CHANGELOG.md
================================================
Change Log
==========
See also [gilrs-core changelog](../gilrs-core/CHANGELOG.md).
v0.11.0 - 2024-09-15
----------
### Breaking changes
- Mark Error enums, `EventType` and `Event` as `non_exhaustive`
### Added
- Added `EventType::ForceFeedbackEffectCompleted`
### Changed
- Minimal supported Rust version is now 1.73
- Updated dependencies
- Updated bundled mappings
### Fixed
- Fixed potential overflow in `btn_value`
v0.10.6 - 2024-03-16
----------
### Fixed
- `axis_dpad_to_button` filter will now properly generate release event when
axis value change from -1 to 1 (or 1 to -1) while skipping 0.
v0.10.5 - 2024-03-06
----------
### Added
- Added `vendor_id()` and `product_id()` to `Gamepad`.
v0.10.4 - 2023-12-03
----------
### Fixed
- Fixed `Gilrs::set_mapping*` returning `MappingError::NotConnected` for
connected gamepads.
- Fix not setting other axis to 0 when in deadzone range.
v0.10.3 - 2023-11-11
----------
### Changed
- All thread spawned by gilrs are now named. (!102)
- MSRV is now 1.65.
v0.10.2 - 2023-04-23
----------
### Added
- `Gilrs::next_event_blocking()`
### Fixed
- Parse more SDL specific buttons in mappings
- Recognize "xinput" UUID in mappings
- Parse axis ranges in button mappings
v0.10.1 - 2022-11-13
----------
### Added
- Supporting files and documentation for running the GUI example using Wasm in
a browser. See [examples/wasm/README.md](./examples/wasm/README.md)
### Changed
- Bundled SDL mappings are now filtered by platform, reducing binary size.
### Fixed
- GUI example crash when the current platform does not support force feedback.
v0.10.0 - 2022-11-06
--------------------
### Changed
- Windows now defaults to using Windows Gaming Input instead of xinput.
If you need to use xInput you can disable the `wgi` feature (It's enabled by
default) and enable the `xinput` feature.
``` toml
gilrs = {version = "0.10.0", default-features = false, features = ["wgi"]}
```
- Apps on Windows will now require a focused window to receive inputs by
default.
This is a limitation of Windows Gaming Input. It requires an in focus Window
be associated with the process to receive events. You can still switch back
to using xInput by turning off default features and enabling the `xinput`
feature.
Note: Some (Older?) devices may still report inputs without a window but this
is not the case for all devices so if you are writing a terminal based game,
use the `xinput` feature instead.
- Minimal supported rust version is now 1.64.
v0.9.0 - 2022-05-22
-------------------
### Changed
- wasm: web-sys/wasm-bindgen is now used by default, dependency on stdweb
and `wasm-bindgen` feature are removed.
- Minimal supported rust version is now 1.56.
- Changed `impl Into\<usize\> for GamepadId`
to `impl From\<GamepadId\> for usize`
### Fixed
- wasm: `next_event()` no longer panic if `getGamepads()` is not available.
v0.8.2 - 2021-12-30
-------------------
### Changed
- Minimal supported rust version is now 1.47
- `axis_dpad_to_btn` now also emits `ButtonChanged` events
### Fixed
- Fixed overflow when calculating axis value and min/max range was
i32::MIN/MAX (@wathiede)
v0.8.1 - 2021-03-30
-------------------
### Changed
- Updated bundled mappings
v0.8.0 - 2020-10-09
-------------------
### Added
- `Jitter`, `Repeat`, `GilrsBuilder`, and `Mapping` now implement `Default`.
- Errors now implement `source()`.
- `Code` now implements `Deserialize` and `Serialize` (@theunkn0wn1).
- Dpad is now supported on macOS (@cleancut).
### Changed
- Minimal supported version is now 1.40
- Non exhaustive enums now use `#[non_exhaustive]` instead of hidden variant.
- Renamed cargo feature `serde` to `serde-serialize`.
- Improved conversion of axis value to float. Values like 127 (when axis range
is 0-255) will now be correctly converted to 0.0.
### Removed
- Errors now longer implement deprecated methods (`source()`
and `description()`).
v0.7.4 - 2020-02-06
-------------------
### Added
- Added method to stop playing force feedback effect. (@photex)
### Fixed
- Fixed bug that caused forced feedback effects to never stop. (@photex)
v0.7.3 - 2019-11-30
-------------------
### Added
- Added support for serialization and deserialization for `Button`, `Axis`
and `AxisOrButton` with optional `serde` feature (@aleksijuvani).
### Fixed
- Fixed defaults mappings containing elements that gamepad doesn't have.
This also fixes state not working for `LeftTrigger` button on Windows.
v0.7.2 - 2019-08-06
-------------------
### Fixed
- Fixed loading mappings for wrong platform
v0.7.1 - 2019-03-04
-------------------
### Fixed
- Compilation on macOS.
- xinput: Calling `set_ff_state()` on devices that were never connected.
- `GamepadId` was not reexported from private module.
v0.7.0 - 2019-02-21
-------------------
### Added
- `GamepadId`
- `Gilrs::gamepad(id)`. This function is replacement
for `Index` operator and can return disconnected gamepads.
- Initial support for macOS (@jtakakura). There are still some functionality
missing, check related issues in #58.
- Wasm support, using stdweb (@ryanisaacg).
### Changed
- Change `Gamepad::uuid -> Uuid` to `Gamepad::uuid -> [u8; 16]`
- `gilrs` now uses `gilrs-core` crate as backend. Because of it,
there are some breaking changes to API.
- Functions that returned `&Gamepad` now return `Gamepad<'_>` proxy object.
- Renamed `Gilrs::get(id)` to `Gilrs::connected_gamepad(id)`.
- Moved `Gamepad::set_mapping{,_strict}()` to `Gilrs`. These functions now
also take gamepad id as additional argument.
- Minimal supported version is now 1.31.1. The crate can still be build with
older rustc, but it may change during next patch release.
- Instead using `usize` for gamepad ID, `GamepadId` is now used.
- Updated bundled SDL_GameControllerDB.
### Removed
- All functions that returned `&mut Gamepad`.
- `Gilrs` no longer implements `Index` and `IndexMut` operators. Use
`Gilrs::gamepad(id)` instead.
- `Gamepad::status()` and `Status` enum. `Gamepad::is_connected()` is
now sufficient to determine status of gamepad.
### Fixed
- xinput: Incorrect gamepad ID when more than one gamepad is connected
(@DTibbs).
- Deadzone filter no longer emits additional events. This resulted in emitting
more events until values normalized on some, often unrelated (like 0 for axis
around 0.5), value.
- Mappings from environment variable had lower priority than bundled mappings.
v0.6.1 - 2018-07-18
-------------------
### Added
- `ev::Code::into_u32()` (@rukai).
- `ev::{Button, Axis, AxisOrBtn}` now implements `Hash` (@sheath).
### Changed
- The URL of repository has changed to https://gitlab.com/gilrs-project/gilrs
- Updated bundled SDL_GameControllerDB.
### Fixed
- Various fixes to logging at incorrect log level. Thanks to @fuggles for
locating and reporting these issues.
- Possible panic in `Repeat` filter.
- `Axis::DPadY` was inverted on Linux.
v0.6.0 - 2018-02-11
-------------------
### Added
- Support for parsing SLD 2.0.6 mappings.
- `ButtonChanged` event. It contains value in range [0.0, 1.0].
- `GilrsBuilder::set_axis_to_btn()`. It allow to customize on which values
`ButtonePressed` and `ButtonReleased` are emitted.
- `GilrsBuilder::set_update_state` which control whether gamepad state should
be updated automatically.
- `ButtonState::value()`.
- `Mapping::insert_{btn,axis}()`.
- `Gampead::os_name()` and `Gamepad::map_name()`. (@rukai)
- `GilrsBuilder::add_env_mappings()`
and `GilrsBuilder::add_included_mappings()`,
allow to configure whether to load mappings from `SDL_GAMECONTROLLERCONFIG`
env
and bundled mappings. (@rukai)
- `Gilrs::insert_event()`.
- `Axis::second_axis()` – returns the other axis of gamepad element. For
example,
this function will return `LeftStickX` for `LeftStickY`.
### Removed
- `Mapping` no longer implements `Index` and `IndexMut` operators. Use
`Mapping::insert_{btn,axis}()` methods to add new mappings.
- `Axis::{LeftTrigger, LeftTrigger2, RightTrigger, RightTrigger2}`. All events
with these are now button events. `ButtonChanged` event contains value.
- `Gilrs::gamepad()` and `Gilrs::gamepad_mut()` – use `Index` operator instead.
### Changed
- Gilrs now require Rust 1.20.0 or newer.
- Updated bundled mappings.
- Renamed `Filter::filter` to `Filter::filter_ev` because RFC 2124 added
`filter` method to `Option` (our `Filter` is implemented
for `Option<Event>`).
- `Gamepad::deadzone()` now returns `Option<f32>` instead of `f32`.
- All axis events are now in range [-1.0, 1.0].
- `NativeEvCode` is replaced by `ev::Code`, a strongly typed struct that also
distinguish between axes and buttons.
- You can now create mappings from any axis to any button.
- `State` now tracks floating-point value of buttons.
- `State::value()` can now be used to also examine value of buttons.
- By default, gamepad state is updated automatically. If you customize event
filters, you can disable this behaviour
using `GilrsBuilder::set_update_state`.
- `Gilrs::new()` and `GilrsBuilder::build()` now returns `Result`. Dummy
context
can still be used, but only if result of failure is unsupported platform.
- Renamed `Gilrs::connected_gamepad()` and `Gilrs::connected_gamepad_mut()` to
`get()` and `get_mut()`.
- `Filter` and `FilterFn` now borrows `Gilrs` mutably.
- Windows: Gamepads are now named "Xbox Controller" instead of "XInput
Controller".
(@rukai)
### Fixed
- Incorrect ranges for some axes.
- Deadzone filter should no longer produce values outside of allowed range.
- When calculating deadzone, the value of second axis is no longer ignored.
This fixes situation, when sometimes axis would stay on value small to 0.0,
when it should be 0.0 instead.
- Deadzone threshold was half of what it should be.
- Linux: Fixed axis value normalization if neither minimal value is 0 nor
midpoint is 0. (@scottpleb)
- Linux: Ensure that axis values are clamped after normalization. (@scottpleb)
- Linux: Compilation error on architectures with `c_char = u8`.
v0.5.0 - 2017-09-24
-------------------
### Added
- `Mapping::remove_button()` and `Mapping::remove_axis()`.
- `GilrsBuilder` for customizing how `Gilrs` is created.
- Event filters. See `ev::filter` module for more info.
- `Gilrs::next_event()` - use it with `while let` loop in your event loop.
This allow to avoid borrow checker problems that `EventIterator` caused.
- New event – `Dropped`. Used by filters to indicate that you should ignore
this event.
- New event – `ButtonRepeated`. Can be emitted by `Repeat` filter.
- `Axis::{DPadX, DPadY}`
- `Gamepad::{button_name, axis_name, button_code, axis_code}` functions for
accessing mapping data.
- `Gamepad::axis_data, button_data` – part of new extended gamepad state.
- `Gamepad::id()` – returns gamepad ID.
- `Gilrs::update, inc, counter, reset_counter` – part of new extended
gamepad state.
### Removed
- `Gilrs::with_mappings()` – use `GilrsBuilder`.
- `Gilrs::poll_events()` and `EventIterator` – use `Gilrs::next_event()`
instead.
### Changed
- Minimal rust version is now 1.19
- New gamepad state. Now can store state for any button or axis (previously was
only useful for named buttons and axes). Additionally it now also know when
last event happened. Basic usage with `is_pressed()` and `value()` methods is
same, but check out documentation for new features.
- Gamepad state now must be explicitly updated with `Gilrs::update(Event)`.
This change was necessary because filters can change events.
- `Event` is now a struct and contains common information like id of gamepad
and timestamp (new). Old enum was renamed to `EventType` and can be accessed
from `Event.event` public field.
- New force feedback module, including support for Windows. There are to many
changes to list them all here, so pleas check documentation and examples.
- Renamed `ff::Error::EffectNotSupported` to `ff::Error::NotSupported`.
- `Button::Unknown` and `Axis::Unknown` have now value of 0.
- `Gamepad::set_mapping()` (and `_strict` variant) now returns error when
creating mapping with `Button::Unknown` or `Axis::Unknown`. Additionally
`_strict` version does not allow `Button::{C, Z}` and Axis::{LeftZ, RightZ}.
- xinput: New values for `NativEvCode`
### Fixed
- Panic on `unreachable!()` when creating mapping with `Button::{C, Z,
Unknown}` or `Axis::{LeftZ, RightZ}`.
v0.4.4 — 2017-06-16
-------------------
### Changed
- Gilrs no longer uses `ioctl` crate on Linux. Because `ioctl` was deprecated
and all versions yanked, it was causing problems for new builds that didn't
have `ioctl` crate listed in Cargo.lock.
v0.4.3 — 2017-03-12
-------------------
### Added
- You can now iterate over mutable references to connected gamepads using
`Gilrs::gamepads_mut()`.
### Fixed
- Fixed `unreachable!()` panic on 32bit Linux
- Improved converting axes values to `f32` when using XInput
v0.4.2 - 2017-01-15
-------------------
### Changed
- Updated SDL_GameControllerDB to latest revision.
- Changes in axes values that are less than 1% are now ignored.
### Fixed
- Fixed multiple axes mapped to same axis name when mappings are incomplete.
- Values returned with `AxisChanged` event now have correctly applied
deadzones.
- Linux: Correctly handle event queue overrun.
v0.4.1 - 2016-12-12
-------------------
### Fixed
- Type inference error introduced by generic index in `<[T]>::get`
v0.4.0 - 2016-12-11
-------------------
### Added
- `Gamepad::mappings_source(&self)` which can be used to filter gamepads which
not provide unified controller layout
- `MappingsSource` enum
- You can now set custom mapping for gamepad with `Gamepad::set_mapping(…)`
- `Gilrs::with_mappings(&str)` to create Gilrs with additional gamepad mappings
### Changed
- Button and axis events now also have native event codes
- On Linux, if button or axis is not known, is now reported as `Unknown`
(previously all unknown events have been ignored)
- More devices are now treated as gamepads on Linux (use `mappings_source()` to
filter unwanted gamepads)
- Renamed `{Gamepad,GamepadState}::is_btn_pressed(Button)` to
`is_pressed(Button)`
- Renamed `{Gamepad,GamepadState}::axis_val(Axis)` to `value(Axis)`
### Fixed
- Integer overflow if button with keyboard code was pressed on Linux
- `Gilrs` should no longer panic if there are some unexpected problems with
Udev
- Fixed normalization of axes values on Linux
v0.3.1 - 2016-09-23
-------------------
### Fixed
- Fixed compilation error on non-x86_64 Linux
v0.3.0 - 2016-09-22
-------------------
### Added
- `Gamepad::power_info(&self)`
- `ff::Direction::from_radians(f32)` and `ff::Direction::from_vector([f32; 2])`
- `Gilrs::gamepads(&self)` which returns iterator over all connected gamepads
- `GamepadState` now implements `is_btn_pressed(Button)` and `axis_val(Axis)`
- `Gilrs` now implements `Index`and `IndexMut`
### Changed
- Rename `Button::Unknow` to `Button::Unknown`
- `Gamepad::name(&self)` now returns `&str` instead of `&String`
- Improved dead zone detection
- `Effect::play(&self, u16)` now returns `Result<(), Error>`
- Linux: Reduced memory usage
### Removed
- `ff::Direction` no longer implements `From<f32>`
### Fixed
- Buttons west and east are no longer swapped when using SDL2 mappings
- Linux: infinite loop after gamepad disconnects
- Linux: SDL2 mappings for gamepads that can also report mouse and keyboard
events now should works
v0.2.0 - 2016-08-18
------
### Changed
- Rename `Gilrs::pool_events()` to `Gilrs::poll_events()`
### Fixed
- Linux: Disconnected events are now emitted properly
- Linux: All force feedback effects are now dropped when gamepad disconnects
================================================
FILE: gilrs/Cargo.toml
================================================
[package]
name = "gilrs"
version = "0.11.0"
authors = ["Arvamer <arvamer@gmail.com>"]
license = "Apache-2.0/MIT"
exclude = ["controller.svg"]
description = "Game Input Library for Rust"
documentation = "https://docs.rs/gilrs/"
repository = "https://gitlab.com/gilrs-project/gilrs"
readme = "../README.md"
keywords = ["gamepad", "joystick", "input"]
categories = ["game-engines"]
edition = "2021"
rust-version = "1.80.0"
[badges]
gitlab = { repository = "gilrs-project/gilrs" }
[dependencies]
vec_map = "0.8"
uuid = "1.0.0"
log = "0.4.1"
fnv = "1.0"
serde = { version = "1.0", features = ["derive"], optional = true }
gilrs-core = { path = "../gilrs-core", version = "0.6.0", default-features = false }
[dev-dependencies]
eframe = "0.30.0"
env_logger = "0.11.5"
console_log = "1.0.0"
egui_plot = "0.30.0"
web-sys = "0.3.76"
wasm-bindgen-futures = "0.4.49"
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
# fixed to avoid msrv bump to 1.81 -- can remove once we're past that
home = "=0.5.9"
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
console_error_panic_hook = "0.1.7"
[package.metadata.docs.rs]
features = ["serde-serialize"]
[features]
default = ["wgi"]
serde-serialize = ["serde", "gilrs-core/serde-serialize"]
xinput = ["gilrs-core/xinput"]
wgi = ["gilrs-core/wgi"]
================================================
FILE: gilrs/build.rs
================================================
//! This build script takes the gamecontrollerdb.txt from the SDL repo and removes any
//! mappings that aren't for the current platform and removes comments etc.
//!
//! This reduces the binary size fairly significantly compared to including mappings for every
//! platform.
//! Especially Wasm since it doesn't use SDL mappings and binary size is important.
use std::env;
use std::fs::File;
use std::io::{BufRead, BufReader, Write};
use std::path::{Path, PathBuf};
#[cfg(windows)]
const PATH_SEPARATOR: &str = "backslash";
#[cfg(not(windows))]
const PATH_SEPARATOR: &str = "slash";
fn main() {
println!("cargo:rustc-check-cfg=cfg(path_separator, values(\"slash\",\"backslash\"))");
println!(r#"cargo:rustc-cfg=path_separator="{}""#, PATH_SEPARATOR);
let out_dir = env::var("OUT_DIR").unwrap();
let cargo_manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let sdl_platform = "platform:".to_string()
+ match env::var("CARGO_CFG_TARGET_FAMILY").unwrap().as_str() {
"unix" => match env::var("CARGO_CFG_TARGET_OS").unwrap().as_str() {
"android" => "Android",
"macos" => "Mac OS X",
_ => "Linux",
},
"windows" => "Windows",
"wasm" => "Web",
_ => "Unknown",
};
let sdl_game_controller_db_path: PathBuf =
PathBuf::from_iter(vec!["SDL_GameControllerDB", "gamecontrollerdb.txt"]);
// Tell cargo to re-run this script only when SDL's gamecontrollerdb.txt changes.
println!(
"cargo:rerun-if-changed={}",
sdl_game_controller_db_path.to_string_lossy()
);
let mut new_file = File::create(Path::new(&out_dir).join("gamecontrollerdb.txt"))
.expect("failed to create gamecontrollerdb.txt for target");
let path = Path::new(&cargo_manifest_dir).join(sdl_game_controller_db_path);
let original_file = File::open(&path).unwrap_or_else(|_| {
panic!(
"Could not open gamecontrollerdb.txt {:?}. Did you forget to pull the \
`SDL_GameControllerDB` submodule?",
&path
)
});
let original_reader = BufReader::new(original_file);
original_reader
.lines()
.map(|x| match x {
Ok(x) => x,
Err(e) => panic!("Failed to read line from gamecontrollerdb.txt: {e}"),
})
.filter(|line| {
line.trim_end()
.trim_end_matches(',')
.ends_with(&sdl_platform)
})
.for_each(|line| {
let mut line = line;
line.push('\n');
new_file
.write_all(line.as_bytes())
.expect("Failed to write line to gamecontrollerdb.txt in OUT_DIR");
});
}
================================================
FILE: gilrs/examples/ev.rs
================================================
// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
use gilrs::ev::filter::{Filter, Repeat};
use gilrs::GilrsBuilder;
use std::process;
fn main() {
env_logger::init();
let mut gilrs = match GilrsBuilder::new().set_update_state(false).build() {
Ok(g) => g,
Err(gilrs::Error::NotImplemented(g)) => {
eprintln!("Current platform is not supported");
g
}
Err(e) => {
eprintln!("Failed to create gilrs context: {}", e);
process::exit(-1);
}
};
let repeat_filter = Repeat::new();
loop {
while let Some(ev) = gilrs
.next_event_blocking(None)
.filter_ev(&repeat_filter, &mut gilrs)
{
gilrs.update(&ev);
println!("{:?}", ev);
}
if gilrs.counter() % 25 == 0 {
for (id, gamepad) in gilrs.gamepads() {
println!(
"Power info of gamepad {}({}): {:?}",
id,
gamepad.name(),
gamepad.power_info()
);
}
}
gilrs.inc();
}
}
================================================
FILE: gilrs/examples/ff.rs
================================================
// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
use gilrs::ff::{BaseEffect, BaseEffectType, EffectBuilder, Replay, Ticks};
use gilrs::Gilrs;
use std::thread;
use std::time::Duration;
fn main() {
env_logger::init();
let mut gilrs = Gilrs::new().unwrap();
let support_ff = gilrs
.gamepads()
.filter_map(|(id, gp)| if gp.is_ff_supported() { Some(id) } else { None })
.collect::<Vec<_>>();
let duration = Ticks::from_ms(150);
let effect = EffectBuilder::new()
.add_effect(BaseEffect {
kind: BaseEffectType::Strong { magnitude: 60_000 },
scheduling: Replay {
play_for: duration,
with_delay: duration * 3,
..Default::default()
},
envelope: Default::default(),
})
.add_effect(BaseEffect {
kind: BaseEffectType::Weak { magnitude: 60_000 },
scheduling: Replay {
after: duration * 2,
play_for: duration,
with_delay: duration * 3,
},
..Default::default()
})
.gamepads(&support_ff)
.finish(&mut gilrs)
.unwrap();
effect.play().unwrap();
thread::sleep(Duration::from_secs(11));
}
================================================
FILE: gilrs/examples/ff_pos.rs
================================================
// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
use gilrs::ff::{BaseEffect, BaseEffectType, DistanceModel, EffectBuilder};
use gilrs::{Axis, Button, EventType, Gilrs};
use std::io::{self, Write};
use std::thread;
use std::time::Duration;
#[derive(Copy, Clone, PartialEq, Debug)]
enum Modify {
DistModel,
RefDistance,
RolloffFactor,
MaxDistance,
}
impl Modify {
fn next(&mut self) {
use crate::Modify::*;
*self = match *self {
DistModel => RefDistance,
RefDistance => RolloffFactor,
RolloffFactor => MaxDistance,
MaxDistance => DistModel,
};
print!("\x1b[2K\r{:?}", self);
io::stdout().flush().unwrap();
}
fn prev(&mut self) {
use crate::Modify::*;
*self = match *self {
DistModel => MaxDistance,
RefDistance => DistModel,
RolloffFactor => RefDistance,
MaxDistance => RolloffFactor,
};
print!("\x1b[2K\r{:?}", self);
io::stdout().flush().unwrap();
}
}
fn main() {
env_logger::init();
let mut gilrs = Gilrs::new().unwrap();
println!("Connected gamepads:");
let mut support_ff = Vec::new();
for (idx, gp) in gilrs.gamepads() {
let ff = gp.is_ff_supported();
println!(
"{}) {} ({})",
idx,
gp.name(),
if ff {
"Force feedback supported"
} else {
"Force feedback not supported"
}
);
if ff {
support_ff.push(idx);
}
}
println!("----------------------------------------");
println!(
"Use sticks to move listener. Triggers change properties of distance model. South/west \
button changes active property. Press east button on action pad to quit."
);
let pos1 = [-100.0, 0.0, 0.0];
let mut effect_builder = EffectBuilder::new()
.add_effect(BaseEffect {
kind: BaseEffectType::Strong { magnitude: 45_000 },
..Default::default()
})
.add_effect(BaseEffect {
kind: BaseEffectType::Weak { magnitude: 45_000 },
..Default::default()
})
.distance_model(DistanceModel::None)
.gamepads(&support_ff)
.clone();
let left_effect = effect_builder.position(pos1).finish(&mut gilrs).unwrap();
left_effect.play().unwrap();
println!("Playing one effects…");
println!("Position of effect sources: {:?}", pos1);
let mut listeners = support_ff
.iter()
.map(|&idx| (idx, [0.0, 0.0, 0.0]))
.collect::<Vec<_>>();
let mut ref_distance = 10.0;
let mut rolloff_factor = 0.5;
let mut max_distance = 100.0;
let mut modify = Modify::DistModel;
let mut model = 0usize;
'main: loop {
while let Some(event) = gilrs.next_event() {
match event.event {
EventType::ButtonReleased(Button::East, ..) => break 'main,
EventType::ButtonReleased(Button::South, ..) => modify.next(),
EventType::ButtonReleased(Button::West, ..) => modify.prev(),
EventType::ButtonReleased(Button::LeftTrigger, ..)
if modify == Modify::DistModel =>
{
model = model.wrapping_sub(1);
}
EventType::ButtonReleased(Button::RightTrigger, ..)
if modify == Modify::DistModel =>
{
model = model.wrapping_add(1);
}
_ => (),
}
}
for &mut (idx, ref mut pos) in &mut listeners {
let velocity = 0.5;
let gp = gilrs.gamepad(idx);
let (sx, sy) = (gp.value(Axis::LeftStickX), gp.value(Axis::LeftStickY));
if sx.abs() > 0.5 || sy.abs() > 0.5 {
if sx.abs() > 0.5 {
pos[0] += velocity * sx.signum();
}
if sy.abs() > 0.5 {
pos[1] += velocity * sy.signum();
}
gilrs.gamepad(idx).set_listener_position(*pos).unwrap();
let dist = ((pos[0] - pos1[0]).powi(2) + (pos[1] - pos1[1]).powi(2)).sqrt();
print!(
"\x1b[2K\rPosition of listener {:2} has changed: [{:6.1}, {:6.1}].Distance: \
{:.1}",
idx, pos[0], pos[1], dist
);
io::stdout().flush().unwrap();
}
let x = if gp.is_pressed(Button::LeftTrigger) {
-1.0
} else if gp.is_pressed(Button::RightTrigger) {
1.0
} else {
continue;
};
match modify {
Modify::RolloffFactor => rolloff_factor += x * velocity * 0.1,
Modify::RefDistance => ref_distance += x * velocity * 0.1,
Modify::MaxDistance => max_distance += x * velocity * 1.0,
Modify::DistModel => (), // DistanceModel handled in event loop
}
let model = match model % 4 {
0 => DistanceModel::None,
1 => DistanceModel::LinearClamped {
ref_distance,
rolloff_factor,
max_distance,
},
2 => DistanceModel::InverseClamped {
ref_distance,
rolloff_factor,
max_distance,
},
3 => DistanceModel::ExponentialClamped {
ref_distance,
rolloff_factor,
max_distance,
},
_ => unreachable!(),
};
match left_effect.set_distance_model(model) {
Ok(()) => print!("\x1b[2K\r{:?}", model),
Err(e) => print!("\x1b[2K\r{}", e),
}
io::stdout().flush().unwrap();
}
thread::sleep(Duration::from_millis(16));
}
}
================================================
FILE: gilrs/examples/gamepad_info.rs
================================================
use gilrs::{Axis, Button, Gilrs};
use uuid::Uuid;
fn main() {
env_logger::init();
let gilrs = Gilrs::new().unwrap();
for (id, gamepad) in gilrs.gamepads() {
println!(
r#"Gamepad {id} ({name}):
Map name: {map_name:?}
Os name: {os_name}
UUID: {uuid}
Is connected: {is_connected}
Power info: {power_info:?}
Mapping source: {mapping_source:?}
Is ff supported: {ff}
Deadzone Left X: {dlx:?}
Deadzone Left Y: {dly:?}
Deadzone Right X: {drx:?}
Deadzone Right Y: {dry:?}
Deadzone Left Trigger: {dlt:?}
Deadzone Right Trigger: {drt:?}
Deadzone Left Trigger 2: {dlt2:?}
Deadzone Right Trigger 2: {drt2:?}
"#,
id = id,
name = gamepad.name(),
map_name = gamepad.map_name(),
os_name = gamepad.os_name(),
uuid = Uuid::from_bytes(gamepad.uuid()).as_hyphenated(),
is_connected = gamepad.is_connected(),
power_info = gamepad.power_info(),
mapping_source = gamepad.mapping_source(),
ff = gamepad.is_ff_supported(),
dlx = gamepad
.axis_code(Axis::LeftStickX)
.and_then(|code| gamepad.deadzone(code)),
dly = gamepad
.axis_code(Axis::LeftStickY)
.and_then(|code| gamepad.deadzone(code)),
drx = gamepad
.axis_code(Axis::RightStickX)
.and_then(|code| gamepad.deadzone(code)),
dry = gamepad
.axis_code(Axis::RightStickY)
.and_then(|code| gamepad.deadzone(code)),
dlt = gamepad
.button_code(Button::LeftTrigger)
.and_then(|code| gamepad.deadzone(code)),
drt = gamepad
.button_code(Button::RightTrigger)
.and_then(|code| gamepad.deadzone(code)),
dlt2 = gamepad
.button_code(Button::LeftTrigger2)
.and_then(|code| gamepad.deadzone(code)),
drt2 = gamepad
.button_code(Button::RightTrigger2)
.and_then(|code| gamepad.deadzone(code)),
);
}
}
================================================
FILE: gilrs/examples/gui.rs
================================================
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
use eframe::egui;
use eframe::egui::Vec2;
use egui::RichText;
use egui_plot::{MarkerShape, Plot, PlotPoints, Points};
use gilrs::ev::AxisOrBtn;
use gilrs::ff::{BaseEffect, BaseEffectType, Effect, EffectBuilder, Repeat, Ticks};
use gilrs::{Axis, GamepadId, Gilrs, GilrsBuilder};
use gilrs_core::PowerInfo;
use std::time::UNIX_EPOCH;
use uuid::Uuid;
struct MyEguiApp {
gilrs: Gilrs,
current_gamepad: Option<GamepadId>,
log_messages: [Option<String>; 300],
// These will be none if Force feedback isn't supported for this platform e.g. Wasm
ff_strong: Option<Effect>,
ff_weak: Option<Effect>,
}
impl Default for MyEguiApp {
fn default() -> Self {
#[cfg(target_arch = "wasm32")]
console_log::init().unwrap();
const INIT: Option<String> = None;
let mut gilrs = GilrsBuilder::new().set_update_state(false).build().unwrap();
let ff_strong = EffectBuilder::new()
.add_effect(BaseEffect {
kind: BaseEffectType::Strong { magnitude: 60_000 },
scheduling: Default::default(),
envelope: Default::default(),
})
.repeat(Repeat::For(Ticks::from_ms(100)))
.finish(&mut gilrs)
.ok();
let ff_weak = EffectBuilder::new()
.add_effect(BaseEffect {
kind: BaseEffectType::Weak { magnitude: 60_000 },
scheduling: Default::default(),
envelope: Default::default(),
})
.repeat(Repeat::For(Ticks::from_ms(100)))
.finish(&mut gilrs)
.ok();
Self {
gilrs,
current_gamepad: None,
log_messages: [INIT; 300],
ff_strong,
ff_weak,
}
}
}
impl MyEguiApp {
fn log(&mut self, message: String) {
self.log_messages[0..].rotate_right(1);
self.log_messages[0] = Some(message);
}
}
impl MyEguiApp {
fn new(_cc: &eframe::CreationContext<'_>) -> Self {
Self::default()
}
}
impl eframe::App for MyEguiApp {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
while let Some(event) = self.gilrs.next_event() {
self.log(format!(
"{} : {} : {:?}",
event
.time
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_millis(),
event.id,
event.event
));
self.gilrs.update(&event);
if self.current_gamepad.is_none() {
self.current_gamepad = Some(event.id);
}
}
egui::SidePanel::left("side_panel").show(ctx, |ui| {
ui.heading("Controllers");
ui.separator();
for (id, gamepad) in self.gilrs.gamepads() {
if ui
.selectable_label(
self.current_gamepad == Some(id),
format!("{id}: {}", gamepad.name()),
)
.clicked()
{
self.current_gamepad = Some(id);
};
}
ui.allocate_space(ui.available_size());
});
egui::TopBottomPanel::bottom("log")
.resizable(true)
.default_height(200.0)
.show(ctx, |ui| {
ui.heading("Event Log");
egui::ScrollArea::vertical()
.max_height(ui.available_height())
.show(ui, |ui| {
for message in self.log_messages.iter().flatten() {
ui.label(message);
}
ui.allocate_space(ui.available_size());
});
});
egui::CentralPanel::default().show(ctx, |ui| {
egui::ScrollArea::both().show(ui, |ui| {
if let Some(gamepad_id) = self.current_gamepad {
let gamepad = self.gilrs.gamepad(gamepad_id);
let gamepad_state = gamepad.state();
ui.horizontal(|ui| {
ui.vertical(|ui| {
ui.heading("Info");
egui::Grid::new("info_grid")
.striped(true)
.num_columns(2)
.show(ui, |ui| {
ui.label("Name");
ui.label(gamepad.name());
ui.end_row();
if let Some(vendor) = gamepad.vendor_id() {
ui.label("Vendor ID");
ui.label(format!("{vendor:04x}"));
ui.end_row();
}
if let Some(product) = gamepad.product_id() {
ui.label("Product ID");
ui.label(format!("{product:04x}"));
ui.end_row();
}
ui.label("Gilrs ID");
ui.label(gamepad.id().to_string());
ui.end_row();
if let Some(map_name) = gamepad.map_name() {
ui.label("Map Name");
ui.label(map_name);
ui.end_row();
}
ui.label("Map Source");
ui.label(format!("{:?}", gamepad.mapping_source()));
ui.end_row();
ui.label("Uuid");
let uuid = Uuid::from_bytes(gamepad.uuid()).to_string();
ui.horizontal(|ui| {
ui.label(&uuid);
if ui.button("Copy").clicked() {
ui.output_mut(|platform_output| {
platform_output.copied_text = uuid;
});
}
});
ui.end_row();
ui.label("Power");
ui.label(match gamepad.power_info() {
PowerInfo::Unknown => "Unknown".to_string(),
PowerInfo::Wired => "Wired".to_string(),
PowerInfo::Discharging(p) => format!("Discharging {p}"),
PowerInfo::Charging(p) => format!("Charging {p}"),
PowerInfo::Charged => "Charged".to_string(),
});
ui.end_row();
});
});
if gamepad.is_ff_supported() {
ui.vertical(|ui| {
ui.label("Force Feedback");
if let Some(ff_strong) = &self.ff_strong {
if ui.button("Play Strong").clicked() {
ff_strong.add_gamepad(&gamepad).unwrap();
ff_strong.play().unwrap();
}
}
if let Some(ff_weak) = &self.ff_weak {
if ui.button("Play Weak").clicked() {
ff_weak.add_gamepad(&gamepad).unwrap();
ff_weak.play().unwrap();
}
}
});
}
});
ui.horizontal(|ui| {
ui.vertical(|ui| {
ui.set_width(300.0);
ui.heading("Buttons");
for (code, button_data) in gamepad_state.buttons() {
let name = match gamepad.axis_or_btn_name(code) {
Some(AxisOrBtn::Btn(b)) => format!("{b:?}"),
_ => "Unknown".to_string(),
};
ui.add(
egui::widgets::ProgressBar::new(button_data.value()).text(
RichText::new(format!(
"{name:<14} {:<5} {:.4} {}",
button_data.is_pressed(),
button_data.value(),
code
))
.monospace(),
),
);
}
});
ui.vertical(|ui| {
ui.set_width(300.0);
ui.heading("Axes");
ui.horizontal(|ui| {
for (name, x, y) in [
("Left Stick", Axis::LeftStickX, Axis::LeftStickY),
("Right Stick", Axis::RightStickX, Axis::RightStickY),
] {
ui.vertical(|ui| {
ui.label(name);
let y_axis = gamepad
.axis_data(y)
.map(|a| a.value())
.unwrap_or_default()
as f64;
let x_axis = gamepad
.axis_data(x)
.map(|a| a.value())
.unwrap_or_default()
as f64;
Plot::new(format!("{name}_plot"))
.width(150.0)
.height(150.0)
.min_size(Vec2::splat(3.25))
.include_x(1.25)
.include_y(1.25)
.include_x(-1.25)
.include_y(-1.25)
.allow_drag(false)
.allow_zoom(false)
.allow_boxed_zoom(false)
.allow_scroll(false)
.show(ui, |plot_ui| {
plot_ui.points(
Points::new(PlotPoints::new(vec![[
x_axis, y_axis,
]]))
.shape(MarkerShape::Circle)
.radius(4.0),
);
});
});
}
});
for (code, axis_data) in gamepad_state.axes() {
let name = match gamepad.axis_or_btn_name(code) {
None => code.to_string(),
Some(AxisOrBtn::Btn(b)) => format!("{b:?}"),
Some(AxisOrBtn::Axis(a)) => format!("{a:?}"),
};
ui.add(
egui::widgets::ProgressBar::new(
(axis_data.value() * 0.5) + 0.5,
)
.text(
RichText::new(format!(
"{:+.4} {name:<15} {}",
axis_data.value(),
code
))
.monospace(),
),
);
}
});
});
} else {
ui.label("Press a button on a controller or select it from the left.");
}
ui.allocate_space(ui.available_size());
});
});
ctx.request_repaint();
}
}
#[cfg(not(target_arch = "wasm32"))]
fn main() {
env_logger::init();
let native_options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default().with_inner_size(Vec2::new(1024.0, 768.0)),
..Default::default()
};
let _ = eframe::run_native(
"Gilrs Input Tester",
native_options,
Box::new(|cc| Ok(Box::new(MyEguiApp::new(cc)))),
);
}
#[cfg(target_arch = "wasm32")]
fn main() {
use eframe::wasm_bindgen::JsCast;
console_error_panic_hook::set_once();
let web_options = eframe::WebOptions::default();
wasm_bindgen_futures::spawn_local(async {
let document = web_sys::window()
.expect("No window")
.document()
.expect("No document");
let canvas = document
.get_element_by_id("the_canvas_id")
.expect("Failed to find the_canvas_id")
.dyn_into::<web_sys::HtmlCanvasElement>()
.expect("the_canvas_id was not a HtmlCanvasElement");
let _ = eframe::WebRunner::new()
.start(
canvas,
web_options,
Box::new(|cc| Ok(Box::new(MyEguiApp::new(cc)))),
)
.await;
});
}
================================================
FILE: gilrs/examples/wasm/.gitignore
================================================
target/
================================================
FILE: gilrs/examples/wasm/README.md
================================================
# Wasm Example
These are instructions for running the GUI example in your web browser using Wasm.
Currently only the GUI example is set up to run with Wasm.
### Ubuntu requirements
```bash
sudo apt install build-essential
sudo apt-get install libssl-dev pkg-config
```
### Setup
```pwsh
rustup target add wasm32-unknown-unknown
cargo install wasm-bindgen-cli
cargo install basic-http-server
```
### Build and Run
Run these from the workspace root.
```pwsh
cargo build --release --example gui --target wasm32-unknown-unknown
wasm-bindgen --out-name wasm_example --out-dir gilrs/examples/wasm/target --target web target/wasm32-unknown-unknown/release/examples/gui.wasm
basic-http-server gilrs/examples/wasm
```
Now open your web browser and navigate to http://127.0.0.1:4000
================================================
FILE: gilrs/examples/wasm/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Gilrs Example</title>
<style>
html {
/* Remove touch delay: */
touch-action: manipulation;
}
body {
/* Light mode background color for what is not covered by the egui canvas,
or where the egui canvas is translucent. */
background: #909090;
}
@media (prefers-color-scheme: dark) {
body {
/* Dark mode background color for what is not covered by the egui canvas,
or where the egui canvas is translucent. */
background: #404040;
}
}
/* Allow canvas to fill entire web page: */
html,
body {
overflow: hidden;
margin: 0 !important;
padding: 0 !important;
height: 100%;
width: 100%;
}
/* Make canvas fill entire document: */
canvas {
margin-right: auto;
margin-left: auto;
display: block;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.centered {
margin-right: auto;
margin-left: auto;
display: block;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #f0f0f0;
font-size: 24px;
font-family: Ubuntu-Light, Helvetica, sans-serif;
text-align: center;
}
</style>
</head>
<!-- The WASM code will resize the canvas dynamically -->
<!-- the id is hardcoded in main.rs . so, make sure both match. -->
<canvas id="the_canvas_id"></canvas>
<script type="module">
import init from './target/wasm_example.js'
init()
</script>
<canvas id="canvas"></canvas>
</html>
================================================
FILE: gilrs/examples/wasm/wasm_gui.ps1
================================================
### This script can be used instead of the "Build and Run" step in `./gilrs/examples/wasm/README.md`.
### Useful for gilrs devs that want a single script to to point their IDE to for run configurations.
### Supports Powershell 5 and up on Windows or Linux
### Make sure to run the install steps from the readme first.
# Start at this script's path and go up three levels to the workspace root.
# Ensures a consistent path regardless of the working directory when you run the script.
$Path = $PSScriptRoot | Split-Path | Split-Path | Split-Path
$ProjectDir = Resolve-Path $Path
Set-Location $ProjectDir
cargo build --release --example gui --target wasm32-unknown-unknown
wasm-bindgen --out-name wasm_example --out-dir gilrs/examples/wasm/target --target web target/wasm32-unknown-unknown/release/examples/gui.wasm
basic-http-server gilrs/examples/wasm
================================================
FILE: gilrs/examples/wasm/wasm_gui.sh
================================================
#!/usr/bin/env bash
### This script can be used instead of the "Build and Run" step in `./gilrs/examples/wasm/README.md`.
### Useful for gilrs devs that want a single script to to point their IDE to for run configurations.
### Make sure to run the install steps from the readme first.
set -e
# Start at this script's path and go up three levels to the workspace root.
# Ensures a consistent path regardless of the working directory when you run the script.
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
PROJECT_DIR=$(dirname "$(dirname "$(dirname "$SCRIPT_DIR")")")
cd "$PROJECT_DIR" || exit
cargo build --release --example gui --target wasm32-unknown-unknown
wasm-bindgen --out-name wasm_example --out-dir gilrs/examples/wasm/target --target web target/wasm32-unknown-unknown/release/examples/gui.wasm
basic-http-server gilrs/examples/wasm
================================================
FILE: gilrs/src/constants.rs
================================================
// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
pub const BTN_UNKNOWN: u16 = 0;
pub const BTN_SOUTH: u16 = 1;
pub const BTN_EAST: u16 = 2;
pub const BTN_C: u16 = 3;
pub const BTN_NORTH: u16 = 4;
pub const BTN_WEST: u16 = 5;
pub const BTN_Z: u16 = 6;
pub const BTN_LT: u16 = 7;
pub const BTN_RT: u16 = 8;
pub const BTN_LT2: u16 = 9;
pub const BTN_RT2: u16 = 10;
pub const BTN_SELECT: u16 = 11;
pub const BTN_START: u16 = 12;
pub const BTN_MODE: u16 = 13;
pub const BTN_LTHUMB: u16 = 14;
pub const BTN_RTHUMB: u16 = 15;
pub const BTN_DPAD_UP: u16 = 16;
pub const BTN_DPAD_DOWN: u16 = 17;
pub const BTN_DPAD_LEFT: u16 = 18;
pub const BTN_DPAD_RIGHT: u16 = 19;
pub const AXIS_UNKNOWN: u16 = 0;
pub const AXIS_LSTICKX: u16 = 1;
pub const AXIS_LSTICKY: u16 = 2;
pub const AXIS_LEFTZ: u16 = 3;
pub const AXIS_RSTICKX: u16 = 4;
pub const AXIS_RSTICKY: u16 = 5;
pub const AXIS_RIGHTZ: u16 = 6;
pub const AXIS_DPADX: u16 = 7;
pub const AXIS_DPADY: u16 = 8;
================================================
FILE: gilrs/src/ev/filter.rs
================================================
// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
//! Alter events in various ways.
//!
//! This modules contains "event filters" that can change, drop or create new events. To use them,
//! import `Filter` trait and call `filter()` function on `Option<Event>`. Because `filter` also
//! returns `Option<Event>` you can combine multiple filters by using `filter()` function on
//! returned event.
//!
//! Filters in this modules have public fields that can be used to configure their behaviour. You
//! can also create them with default values using `new()` method. If filter is not configurable,
//! it is implemented as function (for example `deadzone()`).
//!
//! # Example
//!
//! ```
//! use gilrs::{GilrsBuilder, Filter};
//! use gilrs::ev::filter::{Jitter, Repeat, deadzone};
//!
//! let mut gilrs = GilrsBuilder::new().with_default_filters(false).build().unwrap();
//! let jitter = Jitter { threshold: 0.02 };
//! let repeat = Repeat::new();
//!
//! // Event loop
//! loop {
//! while let Some(event) = gilrs
//! .next_event()
//! .filter_ev(&jitter, &mut gilrs)
//! .filter_ev(&deadzone, &mut gilrs)
//! .filter_ev(&repeat, &mut gilrs)
//! {
//! gilrs.update(&event);
//! println!("{:?}", event);
//! }
//! # break;
//! }
//! ```
//! # Implementing custom filters
//!
//! If you want to implement your own filters, you will have to implement `FilterFn` trait.
//! **Do not return `None` if you got `Some(event)`**. If you want to discard an event, uses
//! `EventType::Dropped`. Returning `None` means that there are no more events to process and
//! will end `while let` loop.
//!
//! ## Example
//!
//! Example implementations of filter that will drop all events with `Unknown` axis or button.
//!
//! ```
//! use gilrs::ev::filter::FilterFn;
//! use gilrs::{Gilrs, Event, EventType, Button, Axis, Filter};
//!
//! struct UnknownSlayer;
//!
//! impl FilterFn for UnknownSlayer {
//! fn filter(&self, ev: Option<Event>, _gilrs: &mut Gilrs) -> Option<Event> {
//! match ev {
//! Some(Event { event: EventType::ButtonPressed(Button::Unknown, ..), id, .. })
//! | Some(Event { event: EventType::ButtonReleased(Button::Unknown, ..), id, .. })
//! | Some(Event { event: EventType::AxisChanged(Axis::Unknown, ..), id, .. })
//! => Some(Event::new(id, EventType::Dropped)),
//! _ => ev,
//! }
//! }
//! }
//! ```
//!
//! `FilterFn` is also implemented for all `Fn(Option<Event>, &Gilrs) -> Option<Event>`, so above
//! example could be simplified to passing closure to `filter()` function.
use crate::ev::{Axis, AxisOrBtn, Button, Code, Event, EventType};
use crate::gamepad::{Gamepad, Gilrs};
use crate::utils;
use std::time::Duration;
/// Discard axis events that changed less than `threshold`.
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct Jitter {
pub threshold: f32,
}
impl Jitter {
/// Creates new `Jitter` filter with threshold set to 0.01.
pub fn new() -> Self {
Jitter { threshold: 0.01 }
}
}
impl Default for Jitter {
fn default() -> Self {
Self::new()
}
}
impl FilterFn for Jitter {
fn filter(&self, ev: Option<Event>, gilrs: &mut Gilrs) -> Option<Event> {
match ev {
Some(Event {
event: EventType::AxisChanged(_, val, axis),
id,
..
}) => match gilrs.gamepad(id).state().axis_data(axis) {
Some(data) if val != 0.0 && (val - data.value()).abs() < self.threshold => {
Some(Event::new(id, EventType::Dropped))
}
_ => ev,
},
_ => ev,
}
}
}
fn apply_deadzone(x: f32, y: f32, threshold: f32) -> (f32, f32) {
let magnitude = utils::clamp((x * x + y * y).sqrt(), 0.0, 1.0);
if magnitude <= threshold {
(0.0, 0.0)
} else {
let norm = ((magnitude - threshold) / (1.0 - threshold)) / magnitude;
(x * norm, y * norm)
}
}
fn deadzone_nonzero_axis_idx(axis: Axis) -> Option<usize> {
Some(match axis {
Axis::DPadX => 0,
Axis::DPadY => 1,
Axis::LeftStickX => 2,
Axis::LeftStickY => 3,
Axis::RightStickX => 4,
Axis::RightStickY => 5,
_ => {
return None;
}
})
}
/// Drops events in dead zone and remaps value to keep it in standard range.
pub fn deadzone(ev: Option<Event>, gilrs: &mut Gilrs) -> Option<Event> {
match ev {
Some(Event {
event: EventType::AxisChanged(axis, val, nec),
id,
time,
}) => {
let threshold = match gilrs.gamepad(id).deadzone(nec) {
Some(t) => t,
None => return ev,
};
if let Some((other_axis, other_code)) = axis
.second_axis()
.and_then(|axis| gilrs.gamepad(id).axis_code(axis).map(|code| (axis, code)))
{
let other_val = gilrs.gamepad(id).state().value(other_code);
let val = apply_deadzone(val, other_val, threshold);
// Since this is the second axis, deadzone_nonzero_axis_idx() will always returns something.
let other_axis_idx = deadzone_nonzero_axis_idx(other_axis).unwrap();
if val.0 == 0.
&& val.1 == 0.
&& gilrs.gamepads_data[id.0].have_sent_nonzero_for_axis[other_axis_idx]
&& gilrs.gamepad(id).state().value(other_code) != 0.
{
// Clear other axis that is now within the dead zone threshold.
gilrs.insert_event(Event {
id,
time,
event: EventType::AxisChanged(other_axis, 0., other_code),
});
gilrs.gamepads_data[id.0].have_sent_nonzero_for_axis[other_axis_idx] = false;
}
Some(if gilrs.gamepad(id).state().value(nec) == val.0 {
Event::new(id, EventType::Dropped)
} else {
if let Some(axis_idx) = deadzone_nonzero_axis_idx(axis) {
gilrs.gamepads_data[id.0].have_sent_nonzero_for_axis[axis_idx] =
val.0 != 0.;
}
Event {
id,
time,
event: EventType::AxisChanged(axis, val.0, nec),
}
})
} else {
let val = apply_deadzone(val, 0.0, threshold).0;
Some(if gilrs.gamepad(id).state().value(nec) == val {
Event::new(id, EventType::Dropped)
} else {
if let Some(axis_idx) = deadzone_nonzero_axis_idx(axis) {
gilrs.gamepads_data[id.0].have_sent_nonzero_for_axis[axis_idx] = val != 0.;
}
Event {
id,
time,
event: EventType::AxisChanged(axis, val, nec),
}
})
}
}
Some(Event {
event: EventType::ButtonChanged(btn, val, nec),
id,
time,
}) => {
let gp = &gilrs.gamepad(id);
let threshold = match gp.deadzone(nec) {
Some(t) => t,
None => return ev,
};
let val = apply_deadzone(val, 0.0, threshold).0;
Some(if gp.state().value(nec) == val {
Event::new(id, EventType::Dropped)
} else {
Event {
id,
time,
event: EventType::ButtonChanged(btn, val, nec),
}
})
}
_ => ev,
}
}
/// Maps axis dpad events to button dpad events.
///
/// This filter will do nothing if gamepad has dpad buttons (to prevent double events for same
/// element) and if standard `NativeEvCode` for dpads is used by some other buttons. It will always
/// try to map if SDL mappings contains mappings for all four hats.
pub fn axis_dpad_to_button(ev: Option<Event>, gilrs: &mut Gilrs) -> Option<Event> {
use gilrs_core::native_ev_codes as necs;
fn can_map(gp: &Gamepad<'_>) -> bool {
let hats_mapped = gp.mapping().hats_mapped();
if hats_mapped == 0b0000_1111 {
true
} else if hats_mapped == 0 {
gp.axis_or_btn_name(Code(necs::BTN_DPAD_RIGHT)).is_none()
&& gp.axis_or_btn_name(Code(necs::BTN_DPAD_LEFT)).is_none()
&& gp.axis_or_btn_name(Code(necs::BTN_DPAD_DOWN)).is_none()
&& gp.axis_or_btn_name(Code(necs::BTN_DPAD_UP)).is_none()
&& gp.button_code(Button::DPadRight).is_none()
} else {
// Not all hats are mapped so let's ignore it for now.
false
}
}
let ev = ev?;
let gamepad = gilrs.gamepad(ev.id);
if !can_map(&gamepad) {
return Some(ev);
}
let mut out_event = ev.drop();
match ev.event {
EventType::AxisChanged(Axis::DPadX, val, _) => {
let mut release_left = false;
let mut release_right = false;
if val == 1.0 {
// The axis value might change from left (-1.0) to right (1.0) immediately without
// us getting an additional event for the release at the center position (0.0).
release_left = gamepad.state().is_pressed(Code(necs::BTN_DPAD_LEFT));
gilrs.insert_event(Event {
event: EventType::ButtonChanged(
Button::DPadRight,
1.0,
Code(necs::BTN_DPAD_RIGHT),
),
..ev
});
out_event = Event {
event: EventType::ButtonPressed(Button::DPadRight, Code(necs::BTN_DPAD_RIGHT)),
..ev
};
} else if val == -1.0 {
// The axis value might change from right (1.0) to left (-1.0) immediately without
// us getting an additional event for the release at the center position (0.0).
release_right = gamepad.state().is_pressed(Code(necs::BTN_DPAD_RIGHT));
gilrs.insert_event(Event {
event: EventType::ButtonChanged(
Button::DPadLeft,
1.0,
Code(necs::BTN_DPAD_LEFT),
),
..ev
});
out_event = Event {
event: EventType::ButtonPressed(Button::DPadLeft, Code(necs::BTN_DPAD_LEFT)),
..ev
};
} else {
release_left = gamepad.state().is_pressed(Code(necs::BTN_DPAD_LEFT));
release_right = gamepad.state().is_pressed(Code(necs::BTN_DPAD_RIGHT));
}
if release_right {
if !out_event.is_dropped() {
gilrs.insert_event(out_event);
}
gilrs.insert_event(Event {
event: EventType::ButtonChanged(
Button::DPadRight,
0.0,
Code(necs::BTN_DPAD_RIGHT),
),
..ev
});
out_event = Event {
event: EventType::ButtonReleased(Button::DPadRight, Code(necs::BTN_DPAD_RIGHT)),
..ev
};
}
if release_left {
if !out_event.is_dropped() {
gilrs.insert_event(out_event);
}
gilrs.insert_event(Event {
event: EventType::ButtonChanged(
Button::DPadLeft,
0.0,
Code(necs::BTN_DPAD_LEFT),
),
..ev
});
out_event = Event {
event: EventType::ButtonReleased(Button::DPadLeft, Code(necs::BTN_DPAD_LEFT)),
..ev
};
}
Some(out_event)
}
EventType::AxisChanged(Axis::DPadY, val, _) => {
let mut release_up = false;
let mut release_down = false;
if val == 1.0 {
// The axis value might change from down (-1.0) to up (1.0) immediately without us
// getting an additional event for the release at the center position (0.0).
release_down = gamepad.state().is_pressed(Code(necs::BTN_DPAD_DOWN));
gilrs.insert_event(Event {
event: EventType::ButtonChanged(Button::DPadUp, 1.0, Code(necs::BTN_DPAD_UP)),
..ev
});
out_event = Event {
event: EventType::ButtonPressed(Button::DPadUp, Code(necs::BTN_DPAD_UP)),
..ev
};
} else if val == -1.0 {
// The axis value might change from up (1.0) to down (-1.0) immediately without us
// getting an additional event for the release at the center position (0.0).
release_up = gamepad.state().is_pressed(Code(necs::BTN_DPAD_UP));
gilrs.insert_event(Event {
event: EventType::ButtonChanged(
Button::DPadDown,
1.0,
Code(necs::BTN_DPAD_DOWN),
),
..ev
});
out_event = Event {
event: EventType::ButtonPressed(Button::DPadDown, Code(necs::BTN_DPAD_DOWN)),
..ev
};
} else {
release_up = gamepad.state().is_pressed(Code(necs::BTN_DPAD_UP));
release_down = gamepad.state().is_pressed(Code(necs::BTN_DPAD_DOWN));
}
if release_up {
if !out_event.is_dropped() {
gilrs.insert_event(out_event);
}
gilrs.insert_event(Event {
event: EventType::ButtonChanged(Button::DPadUp, 0.0, Code(necs::BTN_DPAD_UP)),
..ev
});
out_event = Event {
event: EventType::ButtonReleased(Button::DPadUp, Code(necs::BTN_DPAD_UP)),
..ev
};
}
if release_down {
if !out_event.is_dropped() {
gilrs.insert_event(out_event);
}
gilrs.insert_event(Event {
event: EventType::ButtonChanged(
Button::DPadDown,
0.0,
Code(necs::BTN_DPAD_DOWN),
),
..ev
});
out_event = Event {
event: EventType::ButtonReleased(Button::DPadDown, Code(necs::BTN_DPAD_DOWN)),
..ev
};
}
Some(out_event)
}
_ => Some(ev),
}
}
/// Repeats pressed keys.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Repeat {
pub after: Duration,
pub every: Duration,
}
impl Repeat {
/// Creates new `Repeat` filter with `after` set to 500ms and `every` set to 30ms.
pub fn new() -> Self {
Repeat {
after: Duration::from_millis(500),
every: Duration::from_millis(30),
}
}
}
impl Default for Repeat {
fn default() -> Self {
Self::new()
}
}
impl FilterFn for Repeat {
fn filter(&self, ev: Option<Event>, gilrs: &mut Gilrs) -> Option<Event> {
match ev {
Some(ev) => Some(ev),
None => {
let now = utils::time_now();
for (id, gamepad) in gilrs.gamepads() {
for (nec, btn_data) in gamepad.state().buttons() {
match (
btn_data.is_pressed(),
btn_data.is_repeating(),
now.duration_since(btn_data.timestamp()),
) {
(true, false, Ok(dur)) if dur >= self.after => {
let btn_name = match gamepad.axis_or_btn_name(nec) {
Some(AxisOrBtn::Btn(b)) => b,
_ => Button::Unknown,
};
return Some(Event {
id,
event: EventType::ButtonRepeated(btn_name, nec),
time: btn_data.timestamp() + self.after,
});
}
(true, true, Ok(dur)) if dur >= self.every => {
let btn_name = match gamepad.axis_or_btn_name(nec) {
Some(AxisOrBtn::Btn(b)) => b,
_ => Button::Unknown,
};
return Some(Event {
id,
event: EventType::ButtonRepeated(btn_name, nec),
time: btn_data.timestamp() + self.every,
});
}
_ => (),
}
}
}
None
}
}
}
}
/// Allow filtering events.
///
/// See module level documentation for more info.
pub trait Filter {
fn filter_ev<F: FilterFn>(&self, filter: &F, gilrs: &mut Gilrs) -> Option<Event>;
}
/// Actual filter implementation.
///
/// See module level documentation for more info.
pub trait FilterFn {
fn filter(&self, ev: Option<Event>, gilrs: &mut Gilrs) -> Option<Event>;
}
impl<F> FilterFn for F
where
F: Fn(Option<Event>, &mut Gilrs) -> Option<Event>,
{
fn filter(&self, ev: Option<Event>, gilrs: &mut Gilrs) -> Option<Event> {
self(ev, gilrs)
}
}
impl Filter for Option<Event> {
fn filter_ev<F: FilterFn>(&self, filter: &F, gilrs: &mut Gilrs) -> Option<Event> {
let e = filter.filter(*self, gilrs);
debug_assert!(
!(self.is_some() && e.is_none()),
"Filter changed Some(event) into None. See ev::filter documentation for more info."
);
e
}
}
impl Filter for Event {
fn filter_ev<F: FilterFn>(&self, filter: &F, gilrs: &mut Gilrs) -> Option<Event> {
let e = filter.filter(Some(*self), gilrs);
debug_assert!(
e.is_some(),
"Filter changed Some(event) into None. See ev::filter documentation for more info."
);
e
}
}
================================================
FILE: gilrs/src/ev/mod.rs
================================================
// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
//! Gamepad state and other event related functionality.
pub mod filter;
pub mod state;
use std::{
fmt::{Display, Formatter, Result as FmtResult},
time::SystemTime,
};
use crate::{constants::*, gamepad::GamepadId, utils};
#[cfg(feature = "serde-serialize")]
use serde::{Deserialize, Serialize};
/// Platform specific event code.
///
/// This type represents single gamepads's element like specific axis or button.
/// It can't be directly created, but you can get it from events or using
/// `Gamepad`'s methods [`button_code`](crate::Gamepad::button_code) and
/// [`axis_code`](crate::Gamepad::axis_code). If `serde-serialize` feature is
/// enabled, `Code` can be serialized and deserialized, but keep in mind that
/// layout **is** platform-specific. So it's not possible to serialize `Code` on
/// Linux and deserialize it on Windows. This also apply to `Display` implementation.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
pub struct Code(pub(crate) gilrs_core::EvCode);
impl Display for Code {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
self.0.fmt(f)
}
}
impl Code {
pub fn into_u32(&self) -> u32 {
self.0.into_u32()
}
}
/// Holds information about gamepad event.
#[derive(Copy, Clone, PartialEq, Debug)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
#[non_exhaustive]
pub struct Event {
/// Id of gamepad.
pub id: GamepadId,
/// Event's data.
pub event: EventType,
/// Time when event was emitted.
pub time: SystemTime,
}
impl Event {
/// Creates new event with current time.
pub fn new(id: GamepadId, event: EventType) -> Self {
Event {
id,
event,
time: utils::time_now(),
}
}
/// Returns `Event` with `EventType::Dropped`.
pub fn drop(mut self) -> Event {
self.event = EventType::Dropped;
self
}
/// Returns true if event is `Dropped` and should be ignored.
pub fn is_dropped(&self) -> bool {
self.event == EventType::Dropped
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
#[non_exhaustive]
/// Gamepad event.
pub enum EventType {
/// Some button on gamepad has been pressed.
ButtonPressed(Button, Code),
/// This event can be generated by [`ev::Repeat`](filter/struct.Repeat.html) event filter.
ButtonRepeated(Button, Code),
/// Previously pressed button has been released.
ButtonReleased(Button, Code),
/// Value of button has changed. Value can be in range [0.0, 1.0].
ButtonChanged(Button, f32, Code),
/// Value of axis has changed. Value can be in range [-1.0, 1.0].
AxisChanged(Axis, f32, Code),
/// Gamepad has been connected. If gamepad's UUID doesn't match one of disconnected gamepads,
/// newly connected gamepad will get new ID.
Connected,
/// Gamepad has been disconnected. Disconnected gamepad will not generate any new events.
Disconnected,
/// There was an `Event`, but it was dropped by one of filters. You should ignore it.
Dropped,
/// A force feedback effect has ran for its duration and stopped.
ForceFeedbackEffectCompleted,
}
#[repr(u16)]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
/// Gamepad's elements which state can be represented by value from 0.0 to 1.0.
///
/// 
pub enum Button {
// Action Pad
South = BTN_SOUTH,
East = BTN_EAST,
North = BTN_NORTH,
West = BTN_WEST,
C = BTN_C,
Z = BTN_Z,
// Triggers
LeftTrigger = BTN_LT,
LeftTrigger2 = BTN_LT2,
RightTrigger = BTN_RT,
RightTrigger2 = BTN_RT2,
// Menu Pad
Select = BTN_SELECT,
Start = BTN_START,
Mode = BTN_MODE,
// Sticks
LeftThumb = BTN_LTHUMB,
RightThumb = BTN_RTHUMB,
// D-Pad
DPadUp = BTN_DPAD_UP,
DPadDown = BTN_DPAD_DOWN,
DPadLeft = BTN_DPAD_LEFT,
DPadRight = BTN_DPAD_RIGHT,
#[default]
Unknown = BTN_UNKNOWN,
}
impl Button {
pub fn is_action(self) -> bool {
use crate::Button::*;
matches!(self, South | East | North | West | C | Z)
}
pub fn is_trigger(self) -> bool {
use crate::Button::*;
matches!(
self,
LeftTrigger | LeftTrigger2 | RightTrigger | RightTrigger2
)
}
pub fn is_menu(self) -> bool {
use crate::Button::*;
matches!(self, Select | Start | Mode)
}
pub fn is_stick(self) -> bool {
use crate::Button::*;
matches!(self, LeftThumb | RightThumb)
}
pub fn is_dpad(self) -> bool {
use crate::Button::*;
matches!(self, DPadUp | DPadDown | DPadLeft | DPadRight)
}
pub fn to_nec(self) -> Option<Code> {
use gilrs_core::native_ev_codes as necs;
match self {
Button::South => Some(necs::BTN_SOUTH),
Button::East => Some(necs::BTN_EAST),
Button::North => Some(necs::BTN_NORTH),
Button::West => Some(necs::BTN_WEST),
Button::C => Some(necs::BTN_C),
Button::Z => Some(necs::BTN_Z),
Button::LeftTrigger => Some(necs::BTN_LT),
Button::LeftTrigger2 => Some(necs::BTN_LT2),
Button::RightTrigger => Some(necs::BTN_RT),
Button::RightTrigger2 => Some(necs::BTN_RT2),
Button::Select => Some(necs::BTN_SELECT),
Button::Start => Some(necs::BTN_START),
Button::Mode => Some(necs::BTN_MODE),
Button::LeftThumb => Some(necs::BTN_LTHUMB),
Button::RightThumb => Some(necs::BTN_RTHUMB),
Button::DPadUp => Some(necs::BTN_DPAD_UP),
Button::DPadDown => Some(necs::BTN_DPAD_DOWN),
Button::DPadLeft => Some(necs::BTN_DPAD_LEFT),
Button::DPadRight => Some(necs::BTN_DPAD_RIGHT),
_ => None,
}
.map(Code)
}
}
#[repr(u16)]
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
/// Gamepad's elements which state can be represented by value from -1.0 to 1.0.
///
/// 
pub enum Axis {
LeftStickX = AXIS_LSTICKX,
LeftStickY = AXIS_LSTICKY,
LeftZ = AXIS_LEFTZ,
RightStickX = AXIS_RSTICKX,
RightStickY = AXIS_RSTICKY,
RightZ = AXIS_RIGHTZ,
DPadX = AXIS_DPADX,
DPadY = AXIS_DPADY,
Unknown = AXIS_UNKNOWN,
}
impl Axis {
/// Returns true if axis is `LeftStickX`, `LeftStickY`, `RightStickX` or `RightStickY`.
pub fn is_stick(self) -> bool {
use crate::Axis::*;
matches!(self, LeftStickX | LeftStickY | RightStickX | RightStickY)
}
/// Returns the other axis from same element of gamepad, if any.
///
/// | input | output |
/// |-------------|-------------------|
/// |`LeftStickX` |`Some(LeftStickY)` |
/// |`LeftStickY` |`Some(LeftStickX)` |
/// |`RightStickX`|`Some(RightStickY)`|
/// |`RightStickY`|`Some(RightStickX)`|
/// |`DpadX` |`Some(DpadY)` |
/// |`DpadY` |`Some(DpadX)` |
/// | … |`None` |
pub fn second_axis(self) -> Option<Self> {
use crate::Axis::*;
match self {
LeftStickX => Some(LeftStickY),
LeftStickY => Some(LeftStickX),
RightStickX => Some(RightStickY),
RightStickY => Some(RightStickX),
DPadX => Some(DPadY),
DPadY => Some(DPadX),
_ => None,
}
}
}
/// Represents `Axis` or `Button`.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
pub enum AxisOrBtn {
Axis(Axis),
Btn(Button),
}
impl AxisOrBtn {
pub(crate) fn is_button(&self) -> bool {
matches!(self, AxisOrBtn::Btn(_))
}
}
================================================
FILE: gilrs/src/ev/state.rs
================================================
// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
use crate::ev::Code;
use fnv::FnvHashMap;
use std::collections::hash_map;
use std::iter::Iterator;
use std::time::SystemTime;
/// Cached gamepad state.
#[derive(Clone, Debug)]
pub struct GamepadState {
// Indexed by EvCode (nec)
buttons: FnvHashMap<Code, ButtonData>,
// Indexed by EvCode (nec)
axes: FnvHashMap<Code, AxisData>,
}
impl GamepadState {
pub(crate) fn new() -> Self {
GamepadState {
buttons: FnvHashMap::default(),
axes: FnvHashMap::default(),
}
}
/// Returns `true` if given button is pressed. Returns `false` if there is no information about
/// `btn` or it is not pressed.
pub fn is_pressed(&self, btn: Code) -> bool {
self.buttons
.get(&btn)
.map(|s| s.is_pressed())
.unwrap_or(false)
}
/// Returns value of `el` or 0.0 when there is no information about it. `el` can be either axis
/// or button.
pub fn value(&self, el: Code) -> f32 {
self.axes
.get(&el)
.map(|s| s.value())
.or_else(|| self.buttons.get(&el).map(|s| s.value()))
.unwrap_or(0.0)
}
/// Iterate over buttons data.
pub fn buttons(&self) -> ButtonDataIter<'_> {
ButtonDataIter(self.buttons.iter())
}
/// Iterate over axes data.
pub fn axes(&self) -> AxisDataIter<'_> {
AxisDataIter(self.axes.iter())
}
/// Returns button state and when it changed.
pub fn button_data(&self, btn: Code) -> Option<&ButtonData> {
self.buttons.get(&btn)
}
/// Returns axis state and when it changed.
pub fn axis_data(&self, axis: Code) -> Option<&AxisData> {
self.axes.get(&axis)
}
pub(crate) fn set_btn_pressed(
&mut self,
btn: Code,
pressed: bool,
counter: u64,
timestamp: SystemTime,
) {
let data = self.buttons.entry(btn).or_insert_with(|| {
ButtonData::new(
if pressed { 1.0 } else { 0.0 },
pressed,
false,
counter,
timestamp,
)
});
data.is_pressed = pressed;
data.is_repeating = false;
data.counter = counter;
data.last_event_ts = timestamp;
}
pub(crate) fn set_btn_repeating(&mut self, btn: Code, counter: u64, timestamp: SystemTime) {
let data = self
.buttons
.entry(btn)
.or_insert_with(|| ButtonData::new(1.0, true, true, counter, timestamp));
data.is_repeating = true;
data.counter = counter;
data.last_event_ts = timestamp;
}
pub(crate) fn set_btn_value(
&mut self,
btn: Code,
value: f32,
counter: u64,
timestamp: SystemTime,
) {
let data = self
.buttons
.entry(btn)
.or_insert_with(|| ButtonData::new(value, false, false, counter, timestamp));
data.value = value;
data.counter = counter;
data.last_event_ts = timestamp;
}
pub(crate) fn update_axis(&mut self, axis: Code, data: AxisData) {
self.axes.insert(axis, data);
}
}
/// Iterator over `ButtonData`.
pub struct ButtonDataIter<'a>(hash_map::Iter<'a, Code, ButtonData>);
/// Iterator over `AxisData`.
pub struct AxisDataIter<'a>(hash_map::Iter<'a, Code, AxisData>);
impl<'a> Iterator for ButtonDataIter<'a> {
type Item = (Code, &'a ButtonData);
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|(k, v)| (*k, v))
}
}
impl<'a> Iterator for AxisDataIter<'a> {
type Item = (Code, &'a AxisData);
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|(k, v)| (*k, v))
}
}
/// Information about button stored in `State`.
#[derive(Clone, Copy, Debug)]
pub struct ButtonData {
last_event_ts: SystemTime,
counter: u64,
value: f32,
is_pressed: bool,
is_repeating: bool,
}
impl ButtonData {
pub(crate) fn new(
value: f32,
pressed: bool,
repeating: bool,
counter: u64,
time: SystemTime,
) -> Self {
ButtonData {
last_event_ts: time,
counter,
value,
is_pressed: pressed,
is_repeating: repeating,
}
}
/// Returns `true` if button is pressed.
pub fn is_pressed(&self) -> bool {
self.is_pressed
}
/// Returns value of button.
pub fn value(&self) -> f32 {
self.value
}
/// Returns `true` if button is repeating.
pub fn is_repeating(&self) -> bool {
self.is_repeating
}
/// Returns value of counter when button state last changed.
pub fn counter(&self) -> u64 {
self.counter
}
/// Returns when button state last changed.
pub fn timestamp(&self) -> SystemTime {
self.last_event_ts
}
}
/// Information about axis stored in `State`.
#[derive(Clone, Copy, Debug)]
pub struct AxisData {
last_event_ts: SystemTime,
last_event_c: u64,
value: f32,
}
impl AxisData {
pub(crate) fn new(value: f32, counter: u64, time: SystemTime) -> Self {
AxisData {
last_event_ts: time,
last_event_c: counter,
value,
}
}
/// Returns value of axis.
pub fn value(&self) -> f32 {
self.value
}
/// Returns value of counter when axis value last changed.
pub fn counter(&self) -> u64 {
self.last_event_c
}
/// Returns when axis value last changed.
pub fn timestamp(&self) -> SystemTime {
self.last_event_ts
}
}
================================================
FILE: gilrs/src/ff/base_effect.rs
================================================
// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
use std::ops::Mul;
use super::time::Ticks;
/// Kind of [`BaseEffect`](struct.BaseEffect.html).
///
/// Currently base effect support only xinput model of force feedback, which means that gamepad
/// have weak and strong motor.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[non_exhaustive]
pub enum BaseEffectType {
Weak { magnitude: u16 },
Strong { magnitude: u16 },
}
impl BaseEffectType {
fn magnitude(&self) -> u16 {
match *self {
BaseEffectType::Weak { magnitude } => magnitude,
BaseEffectType::Strong { magnitude } => magnitude,
}
}
}
impl Mul<f32> for BaseEffectType {
type Output = BaseEffectType;
fn mul(self, rhs: f32) -> Self::Output {
let mg = (self.magnitude() as f32 * rhs) as u16;
match self {
BaseEffectType::Weak { .. } => BaseEffectType::Weak { magnitude: mg },
BaseEffectType::Strong { .. } => BaseEffectType::Strong { magnitude: mg },
}
}
}
impl Default for BaseEffectType {
fn default() -> Self {
BaseEffectType::Weak { magnitude: 0 }
}
}
/// Basic building block used to create more complex force feedback effects.
///
/// For each base effect you can specify it's type, for how long should it be played and it's
/// strength during playback.
#[derive(Copy, Clone, PartialEq, Debug, Default)]
pub struct BaseEffect {
/// Type of base effect.
pub kind: BaseEffectType,
/// Defines playback duration and delays between each repetition.
pub scheduling: Replay,
// TODO: maybe allow other f(t)?
/// Basic attenuation function.
pub envelope: Envelope,
}
impl BaseEffect {
/// Returns `Weak` or `Strong` after applying envelope.
pub(super) fn magnitude_at(&self, ticks: Ticks) -> BaseEffectType {
if let Some(wrapped) = self.scheduling.wrap(ticks) {
let att =
self.scheduling.at(wrapped) * self.envelope.at(wrapped, self.scheduling.play_for);
self.kind * att
} else {
self.kind * 0.0
}
}
}
// TODO: Image with "envelope"
#[derive(Copy, Clone, PartialEq, Debug, Default)]
/// Envelope shaped attenuation(time) function.
pub struct Envelope {
pub attack_length: Ticks,
pub attack_level: f32,
pub fade_length: Ticks,
pub fade_level: f32,
}
impl Envelope {
pub(super) fn at(&self, ticks: Ticks, dur: Ticks) -> f32 {
debug_assert!(self.fade_length < dur);
debug_assert!(self.attack_length + self.fade_length < dur);
if ticks < self.attack_length {
self.attack_level
+ ticks.0 as f32 * (1.0 - self.attack_level) / self.attack_length.0 as f32
} else if ticks + self.fade_length > dur {
1.0 + (ticks + self.fade_length - dur).0 as f32 * (self.fade_level - 1.0)
/ self.fade_length.0 as f32
} else {
1.0
}
}
}
/// Defines scheduling of the basic force feedback effect.
///
/// ```text
/// ____________ ____________ ____________
/// | | | | |
/// _______| |____________| |____________|
/// after play_for with_delay play_for with_delay play_for
/// ```
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Replay {
/// Start playback `after` ticks after `Effect::play()` is called.
pub after: Ticks,
/// Playback duration.
pub play_for: Ticks,
/// If playback should be repeated delay it for `with_delay` ticks.
pub with_delay: Ticks,
}
impl Replay {
pub(super) fn at(&self, ticks: Ticks) -> f32 {
if ticks >= self.play_for {
0.0
} else {
1.0
}
}
/// Returns duration of effect calculated as `play_for + with_delay`.
pub fn dur(&self) -> Ticks {
self.play_for + self.with_delay
}
/// Returns `None` if effect hasn't started; or wrapped value
fn wrap(&self, ticks: Ticks) -> Option<Ticks> {
ticks.checked_sub(self.after).map(|t| t % self.dur())
}
}
impl Default for Replay {
fn default() -> Self {
Replay {
after: Ticks(0),
play_for: Ticks(1),
with_delay: Ticks(0),
}
}
}
================================================
FILE: gilrs/src/ff/effect_source.rs
================================================
// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
use std::error::Error;
use std::ops::{AddAssign, Mul};
use std::{fmt, mem};
use crate::{Event, EventType, GamepadId};
use super::base_effect::{BaseEffect, BaseEffectType};
use super::time::{Repeat, Ticks};
use vec_map::VecMap;
/// Specifies how distance between effect source and listener attenuates effect.
///
/// They are based on
/// [OpenAL Specification](http://openal.org/documentation/openal-1.1-specification.pdf) (chapter
/// 3.4), but the best way to see how they differ is to run `ff_pos` example.
///
/// Make sure that all parameters are ≥ 0. Additionally `Linear` and `LinearClamped` models don't
/// like if `ref_distance == max_distance` while others would prefer `ref_distance > 0`.
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub enum DistanceModel {
/// Effect is not attenuated by distance.
#[default]
None,
/// Linear distance model.
Linear {
ref_distance: f32,
rolloff_factor: f32,
max_distance: f32,
},
/// Linear distance clamped model.
LinearClamped {
ref_distance: f32,
rolloff_factor: f32,
max_distance: f32,
},
/// Inverse distance model.
Inverse {
ref_distance: f32,
rolloff_factor: f32,
},
/// Inverse distance clamped model.
InverseClamped {
ref_distance: f32,
rolloff_factor: f32,
max_distance: f32,
},
/// Exponential distance model.
Exponential {
ref_distance: f32,
rolloff_factor: f32,
},
/// Exponential distance clamped model.
ExponentialClamped {
ref_distance: f32,
rolloff_factor: f32,
max_distance: f32,
},
}
impl DistanceModel {
fn attenuation(self, mut distance: f32) -> f32 {
// For now we will follow OpenAL[1] specification for distance models. See chapter 3.4 for
// more details.
//
// [1]: http://openal.org/documentation/openal-1.1-specification.pdf
match self {
DistanceModel::Linear {
ref_distance,
max_distance,
rolloff_factor,
} => {
distance = distance.min(max_distance);
1.0 - rolloff_factor * (distance - ref_distance) / (max_distance - ref_distance)
}
DistanceModel::LinearClamped {
ref_distance,
max_distance,
rolloff_factor,
} => {
distance = distance.max(ref_distance);
distance = distance.min(max_distance);
1.0 - rolloff_factor * (distance - ref_distance) / (max_distance - ref_distance)
}
DistanceModel::Inverse {
ref_distance,
rolloff_factor,
} => ref_distance / (ref_distance + rolloff_factor * (distance - ref_distance)),
DistanceModel::InverseClamped {
ref_distance,
max_distance,
rolloff_factor,
} => {
distance = distance.max(ref_distance);
distance = distance.min(max_distance);
ref_distance / (ref_distance + rolloff_factor * (distance - ref_distance))
}
DistanceModel::Exponential {
ref_distance,
rolloff_factor,
} => (distance / ref_distance).powf(-rolloff_factor),
DistanceModel::ExponentialClamped {
ref_distance,
max_distance,
rolloff_factor,
} => {
distance = distance.max(ref_distance);
distance = distance.min(max_distance);
(distance / ref_distance).powf(-rolloff_factor)
}
DistanceModel::None => 1.0,
}
}
pub(crate) fn validate(self) -> Result<(), DistanceModelError> {
let (ref_distance, rolloff_factor, max_distance) = match self {
DistanceModel::Inverse {
ref_distance,
rolloff_factor,
} => {
if ref_distance <= 0.0 {
return Err(DistanceModelError::InvalidModelParameter);
}
(ref_distance, rolloff_factor, 0.0)
}
DistanceModel::InverseClamped {
ref_distance,
max_distance,
rolloff_factor,
} => {
if ref_distance <= 0.0 {
return Err(DistanceModelError::InvalidModelParameter);
}
(ref_distance, rolloff_factor, max_distance)
}
DistanceModel::Linear {
ref_distance,
max_distance,
rolloff_factor,
} => {
if ref_distance == max_distance {
return Err(DistanceModelError::InvalidModelParameter);
}
(ref_distance, rolloff_factor, max_distance)
}
DistanceModel::LinearClamped {
ref_distance,
max_distance,
rolloff_factor,
} => {
if ref_distance == max_distance {
return Err(DistanceModelError::InvalidModelParameter);
}
(ref_distance, rolloff_factor, max_distance)
}
DistanceModel::Exponential {
ref_distance,
rolloff_factor,
} => {
if ref_distance <= 0.0 {
return Err(DistanceModelError::InvalidModelParameter);
}
(ref_distance, rolloff_factor, 0.0)
}
DistanceModel::ExponentialClamped {
ref_distance,
max_distance,
rolloff_factor,
} => {
if ref_distance <= 0.0 {
return Err(DistanceModelError::InvalidModelParameter);
}
(ref_distance, rolloff_factor, max_distance)
}
DistanceModel::None => (0.0, 0.0, 0.0),
};
if ref_distance < 0.0 {
Err(DistanceModelError::InvalidReferenceDistance)
} else if rolloff_factor < 0.0 {
Err(DistanceModelError::InvalidRolloffFactor)
} else if max_distance < 0.0 {
Err(DistanceModelError::InvalidMaxDistance)
} else {
Ok(())
}
}
}
/// Error that can be returned when passing [`DistanceModel`](struct.DistanceModel.html) with
/// invalid value.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum DistanceModelError {
/// Reference distance is < 0.
InvalidReferenceDistance,
/// Rolloff factor is < 0.
InvalidRolloffFactor,
/// Max distance is < 0.
InvalidMaxDistance,
/// Possible divide by zero
InvalidModelParameter,
}
impl Error for DistanceModelError {}
impl fmt::Display for DistanceModelError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
DistanceModelError::InvalidReferenceDistance => "reference distance is < 0",
DistanceModelError::InvalidRolloffFactor => "rolloff factor is < 0",
DistanceModelError::InvalidMaxDistance => "max distance is < 0",
DistanceModelError::InvalidModelParameter => "possible divide by zero",
};
f.write_str(s)
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub(super) enum EffectState {
Playing { since: Ticks },
Stopped,
}
#[derive(Clone, PartialEq, Debug)]
pub(crate) struct EffectSource {
base_effects: Vec<BaseEffect>,
// TODO: Use bitset
pub(super) devices: VecMap<()>,
pub(super) repeat: Repeat,
pub(super) distance_model: DistanceModel,
pub(super) position: [f32; 3],
pub(super) gain: f32,
pub(super) state: EffectState,
pub(super) completion_events: Vec<Event>,
}
impl EffectSource {
pub(super) fn new(
base_effects: Vec<BaseEffect>,
devices: VecMap<()>,
repeat: Repeat,
dist_model: DistanceModel,
position: [f32; 3],
gain: f32,
) -> Self {
EffectSource {
base_effects,
devices,
repeat,
distance_model: dist_model,
position,
gain,
state: EffectState::Stopped,
completion_events: vec![],
}
}
pub(super) fn combine_base_effects(&mut self, ticks: Ticks, actor_pos: [f32; 3]) -> Magnitude {
let ticks = match self.state {
EffectState::Playing { since } => {
debug_assert!(ticks >= since);
ticks - since
}
EffectState::Stopped => return Magnitude::zero(),
};
match self.repeat {
Repeat::For(max_dur) if ticks > max_dur => {
self.state = EffectState::Stopped;
self.devices.keys().for_each(|id| {
let event = Event::new(GamepadId(id), EventType::ForceFeedbackEffectCompleted);
self.completion_events.push(event);
});
}
_ => (),
}
let attenuation = self
.distance_model
.attenuation(self.position.distance(actor_pos))
* self.gain;
if attenuation < 0.05 {
return Magnitude::zero();
}
let mut final_magnitude = Magnitude::zero();
for effect in &self.base_effects {
match effect.magnitude_at(ticks) {
BaseEffectType::Strong { magnitude } => {
final_magnitude.strong = final_magnitude.strong.saturating_add(magnitude)
}
BaseEffectType::Weak { magnitude } => {
final_magnitude.weak = final_magnitude.weak.saturating_add(magnitude)
}
};
}
final_magnitude * attenuation
}
pub(super) fn flush_completion_events(&mut self) -> Vec<Event> {
mem::take(&mut self.completion_events)
}
}
/// (strong, weak) pair.
#[derive(Copy, Clone, Debug)]
pub(super) struct Magnitude {
pub strong: u16,
pub weak: u16,
}
impl Magnitude {
pub fn zero() -> Self {
Magnitude { strong: 0, weak: 0 }
}
}
impl Mul<f32> for Magnitude {
type Output = Magnitude;
fn mul(self, rhs: f32) -> Self::Output {
debug_assert!(rhs >= 0.0);
let strong = self.strong as f32 * rhs;
let strong = if strong > u16::MAX as f32 {
u16::MAX
} else {
strong as u16
};
let weak = self.weak as f32 * rhs;
let weak = if weak > u16::MAX as f32 {
u16::MAX
} else {
weak as u16
};
Magnitude { strong, weak }
}
}
impl AddAssign for Magnitude {
fn add_assign(&mut self, rhs: Magnitude) {
self.strong = self.strong.saturating_add(rhs.strong);
self.weak = self.weak.saturating_add(rhs.weak);
}
}
trait SliceVecExt {
type Base;
fn distance(self, from: Self) -> Self::Base;
}
impl SliceVecExt for [f32; 3] {
type Base = f32;
fn distance(self, from: Self) -> f32 {
((from[0] - self[0]).powi(2) + (from[1] - self[1]).powi(2) + (from[2] - self[2]).powi(2))
.sqrt()
}
}
================================================
FILE: gilrs/src/ff/mod.rs
================================================
// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
// This code is not used on wasm
#![cfg_attr(target_arch = "wasm32", allow(dead_code))]
//! Force feedback module.
//!
//! To use force feedback, you have to create one or more [`Effect`s](struct.Effect.html). Each
//! `Effect` contains one or more [`BasicEffect`s](struct.BasicEffect.html) and parameters that
//! describe effect's source, like it's position, gain or used
//! [`DistanceModel`](enum.DistanceModel.html). Final strength of effect is based on saturating sum
//! (to `u16::MAX`) of all base effects and time from the start of playback, attenuation from
//! distance between effect source and listener (represented by gamepad) and effect's gain.
//!
//! See also [`Gilrs::set_listener_position()`](../struct.Gilrs.html#method.set_listener_position)
//! and [`Gamepad::is_ff_supported()`](../struct.Gamepad.html#method.is_ff_supported).
//!
//! # Example
//!
//! ```rust
//! use gilrs::Gilrs;
//! use gilrs::ff::{EffectBuilder, Replay, BaseEffect, BaseEffectType, Ticks};
//!
//! let mut gilrs = Gilrs::new().unwrap();
//! let support_ff = gilrs
//! .gamepads()
//! .filter_map(|(id, gp)| if gp.is_ff_supported() { Some(id) } else { None })
//! .collect::<Vec<_>>();
//!
//! let duration = Ticks::from_ms(150);
//! let effect = EffectBuilder::new()
//! .add_effect(BaseEffect {
//! kind: BaseEffectType::Strong { magnitude: 60_000 },
//! scheduling: Replay { play_for: duration, with_delay: duration * 3, ..Default::default() },
//! envelope: Default::default(),
//! })
//! .add_effect(BaseEffect {
//! kind: BaseEffectType::Weak { magnitude: 60_000 },
//! scheduling: Replay { after: duration * 2, play_for: duration, with_delay: duration * 3 },
//! ..Default::default()
//! })
//! .gamepads(&support_ff)
//! .finish(&mut gilrs).unwrap();
//!
//! effect.play().unwrap();
//! ```
//!
//! See [`examples/ff_pos.rs`](https://gitlab.com/gilrs-project/gilrs/blob/v0.11.0/examples/ff_pos.rs) for
//! more advanced example.
mod base_effect;
mod effect_source;
pub(crate) mod server;
mod time;
pub use self::base_effect::{BaseEffect, BaseEffectType, Envelope, Replay};
pub use self::effect_source::{DistanceModel, DistanceModelError};
#[allow(unused_imports)]
pub(crate) use self::time::TICK_DURATION;
pub use self::time::{Repeat, Ticks};
use std::error::Error as StdError;
use std::hash::{Hash, Hasher};
use std::sync::mpsc::{SendError, Sender};
use std::{f32, fmt};
use self::effect_source::EffectSource;
use crate::ff::server::Message;
use crate::gamepad::{Gamepad, GamepadId, Gilrs};
use crate::utils;
use vec_map::VecMap;
/// Handle to force feedback effect.
///
/// `Effect` represents force feedback effect that can be played on one or more gamepads. It uses a
/// form of reference counting, so it can be cheaply cloned. To create new `Effect` use
/// [`EffectBuilder`](struct.EffectBuilder.html).
///
/// All methods on can return `Error::SendFailed` although it shouldn't normally happen.
pub struct Effect {
id: usize,
tx: Sender<Message>,
}
impl PartialEq for Effect {
fn eq(&self, other: &Effect) -> bool {
self.id == other.id
}
}
impl Eq for Effect {}
impl Hash for Effect {
fn hash<H: Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}
impl Clone for Effect {
fn clone(&self) -> Self {
let _ = self.tx.send(Message::HandleCloned { id: self.id });
Effect {
id: self.id,
tx: self.tx.clone(),
}
}
}
impl Drop for Effect {
fn drop(&mut self) {
let _ = self.tx.send(Message::HandleDropped { id: self.id });
}
}
impl Effect {
/// Plays effect on all associated gamepads.
pub fn play(&self) -> Result<(), Error> {
self.tx.send(Message::Play { id: self.id })?;
Ok(())
}
pub fn stop(&self) -> Result<(), Error> {
self.tx.send(Message::Stop { id: self.id })?;
Ok(())
}
/// Changes gamepads that are associated with effect. Effect will be only played on gamepads
/// from last call to this function.
///
/// # Errors
///
/// Returns `Error::Disconnected(id)` or `Error::FfNotSupported(id)` on first gamepad in `ids`
/// that is disconnected or doesn't support force feedback.
pub fn set_gamepads(&self, ids: &[GamepadId], gilrs: &Gilrs) -> Result<(), Error> {
let mut gamepads = VecMap::new();
for dev in ids.iter().cloned() {
if !gilrs
.connected_gamepad(dev)
.ok_or(Error::Disconnected(dev))?
.is_ff_supported()
{
return Err(Error::FfNotSupported(dev));
} else {
gamepads.insert(dev.0, ());
}
}
self.tx.send(Message::SetGamepads {
id: self.id,
gamepads,
})?;
Ok(())
}
/// Adds gamepad to the list of gamepads associated with effect.
///
/// # Errors
///
/// Returns `Error::Disconnected(id)` or `Error::FfNotSupported(id)` if gamepad is not connected
/// or does not support force feedback.
pub fn add_gamepad(&self, gamepad: &Gamepad<'_>) -> Result<(), Error> {
if !gamepad.is_connected() {
Err(Error::Disconnected(gamepad.id()))
} else if !gamepad.is_ff_supported() {
Err(Error::FfNotSupported(gamepad.id()))
} else {
self.tx.send(Message::AddGamepad {
id: self.id,
gamepad_id: gamepad.id(),
})?;
Ok(())
}
}
/// Changes what should happen to effect when it ends.
pub fn set_repeat(&self, repeat: Repeat) -> Result<(), Error> {
self.tx.send(Message::SetRepeat {
id: self.id,
repeat,
})?;
Ok(())
}
/// Changes distance model associated with effect.
///
/// # Errors
///
/// Returns `Error::InvalidDistanceModel` if `model` is not valid. See
/// [`DistanceModel`](enum.DistanceModelError.html) for details.
pub fn set_distance_model(&self, model: DistanceModel) -> Result<(), Error> {
model.validate()?;
self.tx
.send(Message::SetDistanceModel { id: self.id, model })?;
Ok(())
}
/// Changes position of the source of effect.
pub fn set_position<Vec3f: Into<[f32; 3]>>(&self, position: Vec3f) -> Result<(), Error> {
let position = position.into();
self.tx.send(Message::SetPosition {
id: self.id,
position,
})?;
Ok(())
}
/// Changes gain of the effect. `gain` will be clamped to \[0.0, f32::MAX\].
pub fn set_gain(&self, gain: f32) -> Result<(), Error> {
let gain = utils::clamp(gain, 0.0, f32::MAX);
self.tx.send(Message::SetGain { id: self.id, gain })?;
Ok(())
}
}
/// Creates new [`Effect`](struct.Effect.html).
#[derive(Clone, PartialEq, Debug)]
pub struct EffectBuilder {
base_effects: Vec<BaseEffect>,
devices: VecMap<()>,
repeat: Repeat,
dist_model: DistanceModel,
position: [f32; 3],
gain: f32,
}
impl EffectBuilder {
/// Creates new builder with following defaults: no gamepads, no base effects, repeat set to
/// infinitely, no distance model, position in (0.0, 0.0, 0.0) and gain 1.0. Use `finish()` to
/// create new effect.
pub fn new() -> Self {
EffectBuilder {
base_effects: Vec::new(),
devices: VecMap::new(),
repeat: Repeat::Infinitely,
dist_model: DistanceModel::None,
position: [0.0, 0.0, 0.0],
gain: 1.0,
}
}
/// Adds new [`BaseEffect`](struct.BaseEffect.html).
pub fn add_effect(&mut self, effect: BaseEffect) -> &mut Self {
self.base_effects.push(effect);
self
}
/// Changes gamepads that are associated with effect. Effect will be only played on gamepads
/// from last call to this function.
pub fn gamepads(&mut self, ids: &[GamepadId]) -> &mut Self {
for dev in ids {
self.devices.insert(dev.0, ());
}
self
}
/// Adds gamepad to the list of gamepads associated with effect.
pub fn add_gamepad(&mut self, gamepad: &Gamepad<'_>) -> &mut Self {
self.devices.insert(gamepad.id().0, ());
self
}
/// Changes what should happen to effect when it ends.
pub fn repeat(&mut self, repeat: Repeat) -> &mut Self {
self.repeat = repeat;
self
}
/// Changes distance model associated with effect.
pub fn distance_model(&mut self, model: DistanceModel) -> &mut Self {
self.dist_model = model;
self
}
/// Changes position of the source of effect.
pub fn position<Vec3f: Into<[f32; 3]>>(&mut self, position: Vec3f) -> &mut Self {
self.position = position.into();
self
}
/// Changes gain of the effect. `gain` will be clamped to \[0.0, f32::MAX\].
pub fn gain(&mut self, gain: f32) -> &mut Self {
self.gain = utils::clamp(gain, 0.0, f32::MAX);
self
}
/// Validates all parameters and creates new effect.
///
/// # Errors
///
/// Returns `Error::Disconnected(id)` or `Error::FfNotSupported(id)` on first gamepad in `ids`
/// that is disconnected or doesn't support force feedback.
///
/// Returns `Error::InvalidDistanceModel` if `model` is not valid. See
/// [`DistanceModel`](enum.DistanceModelError.html) for details.
pub fn finish(&mut self, gilrs: &mut Gilrs) -> Result<Effect, Error> {
for (dev, _) in &self.devices {
let dev = GamepadId(dev);
if !gilrs
.connected_gamepad(dev)
.ok_or(Error::Disconnected(dev))?
.is_ff_supported()
{
return Err(Error::FfNotSupported(dev));
}
}
self.dist_model.validate()?;
let effect = EffectSource::new(
self.base_effects.clone(),
self.devices.clone(),
self.repeat,
self.dist_model,
self.position,
self.gain,
);
let id = gilrs.next_ff_id();
let tx = gilrs.ff_sender();
tx.send(Message::Create {
id,
effect: Box::new(effect),
})?;
Ok(Effect { id, tx: tx.clone() })
}
}
impl Default for EffectBuilder {
fn default() -> Self {
Self::new()
}
}
/// Basic error type in force feedback module.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum Error {
/// Force feedback is not supported by device with this ID
FfNotSupported(GamepadId),
/// Device is not connected
Disconnected(GamepadId),
/// Distance model is invalid.
InvalidDistanceModel(DistanceModelError),
/// The other end of channel was dropped.
SendFailed,
/// Unexpected error has occurred
Other,
}
impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
match self {
Error::InvalidDistanceModel(m) => Some(m),
_ => None,
}
}
}
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let sbuf;
let s = match self {
Error::FfNotSupported(id) => {
sbuf = format!(
"force feedback is not supported by device with id {}.",
id.0
);
sbuf.as_ref()
}
Error::Disconnected(id) => {
sbuf = format!("device with id {} is not connected.", id.0);
sbuf.as_ref()
}
Error::InvalidDistanceModel(_) => "distance model is invalid",
Error::SendFailed => "receiving end of a channel is disconnected.",
Error::Other => "unespected error has occurred.",
};
fmt.write_str(s)
}
}
impl<T> From<SendError<T>> for Error {
fn from(_: SendError<T>) -> Self {
Error::SendFailed
}
}
impl From<DistanceModelError> for Error {
fn from(f: DistanceModelError) -> Self {
Error::InvalidDistanceModel(f)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn envelope() {
let env = Envelope {
attack_length: Ticks(10),
attack_level: 0.2,
fade_length: Ticks(10),
fade_level: 0.2,
};
let dur = Ticks(40);
assert_eq!(env.at(Ticks(0), dur), 0.2);
assert_eq!(env.at(Ticks(5), dur), 0.6);
assert_eq!(env.at(Ticks(10), dur), 1.0);
assert_eq!(env.at(Ticks(20), dur), 1.0);
assert_eq!(env.at(Ticks(30), dur), 1.0);
assert_eq!(env.at(Ticks(35), dur), 0.6);
assert_eq!(env.at(Ticks(40), dur), 0.19999999);
}
#[test]
fn envelope_default() {
let env = Envelope::default();
let dur = Ticks(40);
assert_eq!(env.at(Ticks(0), dur), 1.0);
assert_eq!(env.at(Ticks(20), dur), 1.0);
assert_eq!(env.at(Ticks(40), dur), 1.0);
}
#[test]
fn replay() {
let replay = Replay {
after: Ticks(10),
play_for: Ticks(50),
with_delay: Ticks(20),
};
assert_eq!(replay.at(Ticks(0)), 1.0);
assert_eq!(replay.at(Ticks(9)), 1.0);
assert_eq!(replay.at(Ticks(10)), 1.0);
assert_eq!(replay.at(Ticks(30)), 1.0);
assert_eq!(replay.at(Ticks(59)), 0.0);
assert_eq!(replay.at(Ticks(60)), 0.0);
assert_eq!(replay.at(Ticks(70)), 0.0);
}
}
================================================
FILE: gilrs/src/ff/server.rs
================================================
// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
use super::effect_source::{DistanceModel, EffectSource, EffectState, Magnitude};
use super::time::{Repeat, Ticks, TICK_DURATION};
use std::ops::{Deref, DerefMut};
use std::sync::mpsc::{self, Receiver, Sender};
use std::thread;
use std::time::{Duration, Instant};
use crate::gamepad::GamepadId;
use crate::Event;
use gilrs_core::FfDevice;
use vec_map::VecMap;
#[derive(Debug)]
pub(crate) enum Message {
Create {
id: usize,
effect: Box<EffectSource>,
},
HandleCloned {
id: usize,
},
HandleDropped {
id: usize,
},
Play {
id: usize,
},
Stop {
id: usize,
},
Open {
id: usize,
device: FfDevice,
},
Close {
id: usize,
},
SetListenerPosition {
id: usize,
position: [f32; 3],
},
SetGamepads {
id: usize,
gamepads: VecMap<()>,
},
AddGamepad {
id: usize,
gamepad_id: GamepadId,
},
SetRepeat {
id: usize,
repeat: Repeat,
},
SetDistanceModel {
id: usize,
model: DistanceModel,
},
SetPosition {
id: usize,
position: [f32; 3],
},
SetGain {
id: usize,
gain: f32,
},
}
pub(crate) enum FfMessage {
EffectCompleted { event: Event },
}
impl Message {
// Whether to use trace level logging or debug
fn use_trace_level(&self) -> bool {
use self::Message::*;
matches!(
self,
&SetListenerPosition { .. } | &HandleCloned { .. } | &HandleDropped { .. }
)
}
}
#[derive(Debug)]
struct Device {
inner: FfDevice,
position: [f32; 3],
}
struct Effect {
source: EffectSource,
/// Number of created effect's handles.
count: usize,
}
impl Effect {
fn inc(&mut self) -> usize {
self.count += 1;
self.count
}
fn dec(&mut self) -> usize {
self.count -= 1;
self.count
}
}
impl From<EffectSource> for Effect {
fn from(source: EffectSource) -> Self {
Effect { source, count: 1 }
}
}
impl Deref for Effect {
type Target = EffectSource;
fn deref(&self) -> &Self::Target {
&self.source
}
}
impl DerefMut for Effect {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.source
}
}
impl From<FfDevice> for Device {
fn from(inner: FfDevice) -> Self {
Device {
inner,
position: [0.0, 0.0, 0.0],
}
}
}
pub(crate) fn run(tx: Sender<FfMessage>, rx: Receiver<Message>) {
let mut effects = VecMap::<Effect>::new();
let mut devices = VecMap::<Device>::new();
let sleep_dur = Duration::from_millis(TICK_DURATION.into());
let mut tick = Ticks(0);
let mut completion_events = Vec::<Event>::new();
loop {
let t1 = Instant::now();
while let Ok(ev) = rx.try_recv() {
if ev.use_trace_level() {
trace!("New ff event: {:?}", ev);
} else {
debug!("New ff event: {:?}", ev);
}
match ev {
Message::Create { id, effect } => {
effects.insert(id, (*effect).into());
}
Message::Play { id } => {
if let Some(effect) = effects.get_mut(id) {
effect.source.state = EffectState::Playing { since: tick }
} else {
error!("{:?} with wrong ID", ev);
}
}
Message::Stop { id } => {
if let Some(effect) = effects.get_mut(id) {
effect.source.state = EffectState::Stopped
} else {
error!("{:?} with wrong ID", ev);
}
}
Message::Open { id, device } => {
devices.insert(id, device.into());
}
Message::Close { id } => {
devices.remove(id);
}
Message::SetListenerPosition { id, position } => {
if let Some(device) = devices.get_mut(id) {
device.position = position;
} else {
error!("{:?} with wrong ID", ev);
}
}
Message::HandleCloned { id } => {
if let Some(effect) = effects.get_mut(id) {
effect.inc();
} else {
error!("{:?} with wrong ID", ev);
}
}
Message::HandleDropped { id } => {
let mut drop = false;
if let Some(effect) = effects.get_mut(id) {
if effect.dec() == 0 {
drop = true;
}
} else {
error!("{:?} with wrong ID", ev);
}
if drop {
effects.remove(id);
}
}
Message::SetGamepads { id, gamepads } => {
if let Some(eff) = effects.get_mut(id) {
eff.source.devices = gamepads;
} else {
error!("Invalid effect id {} when changing gamepads.", id);
}
}
Message::AddGamepad { id, gamepad_id } => {
if let Some(eff) = effects.get_mut(id) {
eff.source.devices.insert(gamepad_id.0, ());
} else {
error!("Invalid effect id {} when changing gamepads.", id);
}
}
Message::SetRepeat { id, repeat } => {
if let Some(eff) = effects.get_mut(id) {
eff.source.repeat = repeat;
} else {
error!("Invalid effect id {} when changing repeat mode.", id);
}
}
Message::SetDistanceModel { id, model } => {
if let Some(eff) = effects.get_mut(id) {
eff.source.distance_model = model;
} else {
error!("Invalid effect id {} when changing distance model.", id);
}
}
Message::SetPosition { id, position } => {
if let Some(eff) = effects.get_mut(id) {
eff.source.position = position;
} else {
error!("Invalid effect id {}.", id);
}
}
Message::SetGain { id, gain } => {
if let Some(eff) = effects.get_mut(id) {
eff.source.gain = gain;
} else {
error!("Invalid effect id {} when changing effect gain.", id);
}
}
}
}
combine_and_play(&mut effects, &mut devices, tick, &mut completion_events);
completion_events.iter().for_each(|ev| {
let _ = tx.send(FfMessage::EffectCompleted { event: *ev });
});
completion_events.clear();
let dur = Instant::now().duration_since(t1);
if dur > sleep_dur {
// TODO: Should we add dur - sleep_dur to next iteration's dur?
warn!(
"One iteration of a force feedback loop took more than {}ms!",
TICK_DURATION
);
} else {
thread::sleep(sleep_dur - dur);
}
tick.inc();
}
}
pub(crate) fn init() -> (Sender<Message>, Receiver<FfMessage>) {
let (tx, _rx) = mpsc::channel();
let (_tx2, rx2) = mpsc::channel();
// Wasm doesn't support threads and force feedback
#[cfg(not(target_arch = "wasm32"))]
std::thread::Builder::new()
.name("gilrs".to_owned())
.spawn(move || run(_tx2, _rx))
.expect("failed to spawn thread");
(tx, rx2)
}
fn combine_and_play(
effects: &mut VecMap<Effect>,
devices: &mut VecMap<Device>,
tick: Ticks,
completion_events: &mut Vec<Event>,
) {
for (dev_id, dev) in devices {
let mut magnitude = Magnitude::zero();
for (_, ref mut effect) in effects.iter_mut() {
if effect.devices.contains_key(dev_id) {
magnitude += effect.combine_base_effects(tick, dev.position);
completion_events.extend(effect.flush_completion_events());
}
}
trace!(
"({:?}) Setting ff state of {:?} to {:?}",
tick,
dev,
magnitude
);
dev.inner.set_ff_state(
magnitude.strong,
magnitude.weak,
Duration::from_millis(u64::from(TICK_DURATION) * 2),
);
}
}
================================================
FILE: gilrs/src/ff/time.rs
================================================
// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
use std::ops::{Add, AddAssign, Mul, MulAssign, Rem, Sub, SubAssign};
use std::time::Duration;
use crate::utils;
pub(crate) const TICK_DURATION: u32 = 50;
/// Represents duration.
///
/// This type is only useful as input parameter for other functions in force feedback module. To
/// create it, use `from_ms()` method. Keep in mind that `Ticks` **is not precise** representation
/// of time.
///
/// # Example
///
/// ```rust
/// use gilrs::ff::Ticks;
/// use std::time::Duration;
///
/// let t1 = Ticks::from_ms(110);
/// let t2 = Ticks::from(Duration::from_millis(130));
///
/// /// `Ticks` is not precise.
/// assert_eq!(t1, t2);
/// ```
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Default)]
pub struct Ticks(pub(super) u32);
impl Ticks {
pub fn from_ms(dur: u32) -> Self {
Ticks(utils::ceil_div(dur, TICK_DURATION))
}
pub(super) fn inc(&mut self) {
self.0 += 1
}
pub(super) fn checked_sub(self, rhs: Ticks) -> Option<Ticks> {
self.0.checked_sub(rhs.0).map(Ticks)
}
}
impl From<Duration> for Ticks {
fn from(dur: Duration) -> Self {
Ticks::from_ms(dur.as_secs() as u32 * 1000 + dur.subsec_millis())
}
}
impl Add for Ticks {
type Output = Ticks;
fn add(self, rhs: Ticks) -> Self::Output {
Ticks(self.0 + rhs.0)
}
}
impl AddAssign for Ticks {
fn add_assign(&mut self, rhs: Ticks) {
self.0 += rhs.0
}
}
impl Sub for Ticks {
type Output = Ticks;
fn sub(self, rhs: Ticks) -> Self::Output {
Ticks(self.0 - rhs.0)
}
}
impl SubAssign for Ticks {
fn sub_assign(&mut self, rhs: Ticks) {
self.0 -= rhs.0
}
}
impl Mul<u32> for Ticks {
type Output = Ticks;
fn mul(self, rhs: u32) -> Self::Output {
Ticks(self.0 * rhs)
}
}
impl MulAssign<u32> for Ticks {
fn mul_assign(&mut self, rhs: u32) {
self.0 *= rhs;
}
}
impl Rem for Ticks {
type Output = Ticks;
fn rem(self, rhs: Ticks) -> Self::Output {
Ticks(self.0 % rhs.0)
}
}
/// Describes how long effect should be played.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum Repeat {
/// Play effect until stop() is called.
#[default]
Infinitely,
/// Play effect for specified time.
For(Ticks),
}
================================================
FILE: gilrs/src/gamepad.rs
================================================
// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
use crate::{
ev::{
state::{AxisData, ButtonData, GamepadState},
Axis, AxisOrBtn, Button, Code, Event, EventType,
},
ff::{
server::{self, FfMessage, Message},
Error as FfError,
},
mapping::{Mapping, MappingData, MappingDb},
utils, MappingError,
};
use gilrs_core::{
self, AxisInfo, Error as PlatformError, Event as RawEvent, EventType as RawEventType,
};
use uuid::Uuid;
use std::cmp::Ordering;
use std::{
collections::VecDeque,
error,
fmt::{self, Display},
sync::mpsc::{Receiver, Sender},
time::Duration,
};
pub use gilrs_core::PowerInfo;
#[cfg(feature = "serde-serialize")]
use serde::{Deserialize, Serialize};
const DEFAULT_DEADZONE: f32 = 0.1;
/// Main object responsible of managing gamepads.
///
/// In order to get gamepad handle, use `gamepad()`, or `connected_gamepad()`. The main difference
/// between these two is that `gamepad()` will also return handle to gamepad that is currently
/// disconnected. However, both functions will return `None` if gamepad with given id has never
/// existed.
///
/// # Event loop
///
/// All interesting actions like button was pressed or new controller was connected are represented
/// by struct [`Event`](struct.Event.html). Use `next_event()` function to retrieve event from
/// queue.
///
/// ```
/// use gilrs::{Gilrs, Event, EventType, Button};
///
/// let mut gilrs = Gilrs::new().unwrap();
///
/// // Event loop
/// loop {
/// while let Some(event) = gilrs.next_event() {
/// match event {
/// Event { id, event: EventType::ButtonPressed(Button::South, _), .. } => {
/// println!("Player {}: jump!", id)
/// }
/// Event { id, event: EventType::Disconnected, .. } => {
/// println!("We lost player {}", id)
/// }
/// _ => (),
/// };
/// }
/// # break;
/// }
/// ```
///
/// # Cached gamepad state
///
/// `Gilrs` also menage cached gamepad state. Updating state is done automatically, unless it's
/// disabled by `GilrsBuilder::set_update_state(false)`. However, if you are using custom filters,
/// you still have to update state manually – to do this call `update()` method.
///
/// To access state you can use `Gamepad::state()` function. Gamepad also implement some state
/// related functions directly, see [`Gamepad`](struct.Gamepad.html) for more.
///
/// ## Counter
///
/// `Gilrs` has additional functionality, referred here as *counter*. The idea behind it is simple,
/// each time you end iteration of update loop, you call `Gilrs::inc()` which will increase
/// internal counter by one. When state of one if elements changes, value of counter is saved. When
/// checking state of one of elements you can tell exactly when this event happened. Timestamps are
/// not good solution here because they can tell you when *system* observed event, not when you
/// processed it. On the other hand, they are good when you want to implement key repeat or software
/// debouncing.
///
/// ```
/// use gilrs::{Gilrs, Button};
///
/// let mut gilrs = Gilrs::new().unwrap();
/// let mut player_one = None;
///
/// loop {
/// while let Some(ev) = gilrs.next_event() {
/// if player_one.is_none() {
/// player_one = Some(ev.id);
/// }
///
/// // Do other things with event
/// }
///
/// if let Some(id) = player_one {
/// let gamepad = gilrs.gamepad(id);
///
/// if gamepad.is_pressed(Button::DPadLeft) {
/// // go left
/// }
///
/// match gamepad.button_data(Button::South) {
/// Some(d) if d.is_pressed() && d.counter() == gilrs.counter() => {
/// // jump only if button was observed to be pressed in this iteration
/// }
/// _ => ()
/// }
/// }
///
/// // Increase counter
/// gilrs.inc();
/// # break;
/// }
///
#[derive(Debug)]
pub struct Gilrs {
inner: gilrs_core::Gilrs,
next_id: usize,
tx: Sender<Message>,
rx: Receiver<FfMessage>,
counter: u64,
mappings: MappingDb,
default_filters: bool,
events: VecDeque<Event>,
axis_to_btn_pressed: f32,
axis_to_btn_released: f32,
pub(crate) update_state: bool,
pub(crate) gamepads_data: Vec<GamepadData>,
}
impl Gilrs {
/// Creates new `Gilrs` with default settings. See [`GilrsBuilder`](struct.GilrsBuilder.html)
/// for more details.
pub fn new() -> Result<Self, Error> {
GilrsBuilder::new().build()
}
/// Returns next pending event. If there is no pending event, `None` is
/// returned. This function will not block current thread and should be safe
/// to call in async context. Doesn't block the thread it is run in
pub fn next_event(&mut self) -> Option<Event> {
self.next_event_inner(false, None)
}
/// Same as [Gilrs::next_event], but blocks the thread it is run in. Useful
/// for apps that aren't run inside a loop and just react to the user's input,
/// like GUI apps.
///
/// ## Platform support
///
/// This function is not supported on web and will always panic.
pub fn next_event_blocking(&mut self, timeout: Option<Duration>) -> Option<Event> {
self.next_event_inner(true, timeout)
}
fn next_event_inner(
&mut self,
is_blocking: bool,
blocking_timeout: Option<Duration>,
) -> Option<Event> {
use crate::ev::filter::{axis_dpad_to_button, deadzone, Filter, Jitter};
let ev = if self.default_filters {
let jitter_filter = Jitter::new();
loop {
let ev = self
.next_event_priv(is_blocking, blocking_timeout)
.filter_ev(&axis_dpad_to_button, self)
.filter_ev(&jitter_filter, self)
.filter_ev(&deadzone, self);
// Skip all dropped events, there is no reason to return them
match ev {
Some(ev) if ev.is_dropped() => (),
_ => break ev,
}
}
} else {
self.next_event_priv(is_blocking, blocking_timeout)
};
if self.update_state {
if let Some(ref ev) = ev {
self.update(ev);
}
}
ev
}
/// Returns next pending event.
fn next_event_priv(
&mut self,
is_blocking: bool,
blocking_timeout: Option<Duration>,
) -> Option<Event> {
if let Ok(msg) = self.rx.try_recv() {
return match msg {
FfMessage::EffectCompleted { event } => Some(event),
}
}
if let Some(ev) = self.events.pop_front() {
Some(ev)
} else {
let event = if is_blocking {
self.inner.next_event_blocking(blocking_timeout)
} else {
self.inner.next_event()
};
match event {
Some(RawEvent {
id,
event: event_type,
time,
..
}) => {
trace!("Original event: {:?}", event);
let id = GamepadId(id);
let event = match event_type {
RawEventType::ButtonPressed(nec) => {
let nec = Code(nec);
match self.gamepad(id).axis_or_btn_name(nec) {
Some(AxisOrBtn::Btn(b)) => {
self.events.push_back(Event {
id,
time,
event: EventType::ButtonChanged(b, 1.0, nec),
});
EventType::ButtonPressed(b, nec)
}
Some(AxisOrBtn::Axis(a)) => EventType::AxisChanged(a, 1.0, nec),
None => {
self.events.push_back(Event {
id,
time,
event: EventType::ButtonChanged(Button::Unknown, 1.0, nec),
});
EventType::ButtonPressed(Button::Unknown, nec)
}
}
}
RawEventType::ButtonReleased(nec) => {
let nec = Code(nec);
match self.gamepad(id).axis_or_btn_name(nec) {
Some(AxisOrBtn::Btn(b)) => {
self.events.push_back(Event {
id,
time,
event: EventType::ButtonChanged(b, 0.0, nec),
});
EventType::ButtonReleased(b, nec)
}
Some(AxisOrBtn::Axis(a)) => EventType::AxisChanged(a, 0.0, nec),
None => {
self.events.push_back(Event {
id,
time,
event: EventType::ButtonChanged(Button::Unknown, 0.0, nec),
});
EventType::ButtonReleased(Button::Unknown, nec)
}
}
}
RawEventType::AxisValueChanged(val, nec) => {
// Let's trust at least our backend code
let axis_info = *self.gamepad(id).inner.axis_info(nec).unwrap();
let nec = Code(nec);
match self.gamepad(id).axis_or_btn_name(nec) {
Some(AxisOrBtn::Btn(b)) => {
let val = btn_value(&axis_info, val);
if val >= self.axis_to_btn_pressed
&& !self.gamepad(id).state().is_pressed(nec)
{
self.events.push_back(Event {
id,
time,
event: EventType::ButtonChanged(b, val, nec),
});
EventType::ButtonPressed(b, nec)
} else if val <= self.axis_to_btn_released
&& self.gamepad(id).state().is_pressed(nec)
{
self.events.push_back(Event {
id,
time,
event: EventType::ButtonChanged(b, val, nec),
});
EventType::ButtonReleased(b, nec)
} else {
EventType::ButtonChanged(b, val, nec)
}
}
Some(AxisOrBtn::Axis(a)) => {
EventType::AxisChanged(a, axis_value(&axis_info, val, a), nec)
}
None => EventType::AxisChanged(
Axis::Unknown,
axis_value(&axis_info, val, Axis::Unknown),
nec,
),
}
}
RawEventType::Connected => {
match id.0.cmp(&self.gamepads_data.len()) {
Ordering::Equal => {
self.gamepads_data.push(GamepadData::new(
id,
self.tx.clone(),
self.inner.gamepad(id.0).unwrap(),
&self.mappings,
));
}
Ordering::Less => {
self.gamepads_data[id.0] = GamepadData::new(
id,
self.tx.clone(),
self.inner.gamepad(id.0).unwrap(),
&self.mappings,
);
}
Ordering::Greater => {
error!(
"Platform implementation error: got Connected event with \
id {}, when expected id {}",
id.0,
self.gamepads_data.len()
);
}
}
EventType::Connected
}
RawEventType::Disconnected => {
let _ = self.tx.send(Message::Close { id: id.0 });
EventType::Disconnected
}
_ => {
unimplemented!()
}
};
Some(Event { id, event, time })
}
None => None,
}
}
}
/// Updates internal state according to `event`.
///
/// Please note, that it's not necessary to call this function unless you modify events by using
/// additional filters and disabled automatic updates when creating `Gilrs`.
pub fn update(&mut self, event: &Event) {
use crate::EventType::*;
let counter = self.counter;
let data = match self.gamepads_data.get_mut(event.id.0) {
Some(d) => d,
None => return,
};
match event.event {
ButtonPressed(_, nec) => {
data.state.set_btn_pressed(nec, true, counter, event.time);
}
ButtonReleased(_, nec) => {
data.state.set_btn_pressed(nec, false, counter, event.time);
}
ButtonRepeated(_, nec) => {
data.state.set_btn_repeating(nec, counter, event.time);
}
ButtonChanged(_, value, nec) => {
data.state.set_btn_value(nec, value, counter, event.time);
}
AxisChanged(_, value, nec) => {
data.state
.update_axis(nec, AxisData::new(value, counter, event.time));
}
Disconnected | Connected | Dropped | ForceFeedbackEffectCompleted => (),
}
}
/// Increases internal counter by one. Counter data is stored with state and can be used to
/// determine when last event happened. You probably want to use this function in your update
/// loop after processing events.
pub fn inc(&mut self) {
// Counter is 62bit. See `ButtonData`.
if self.counter == 0x3FFF_FFFF_FFFF_FFFF {
self.counter = 0;
} else {
self.counter += 1;
}
}
/// Returns counter. Counter data is stored with state and can be used to determine when last
/// event happened.
pub fn counter(&self) -> u64 {
self.counter
}
/// Sets counter to 0.
pub fn reset_counter(&mut self) {
self.counter = 0;
}
fn finish_gamepads_creation(&mut self) {
let tx = self.tx.clone();
for id in 0..self.inner.last_gamepad_hint() {
let gamepad = self.inner.gamepad(id).unwrap();
self.gamepads_data.push(GamepadData::new(
GamepadId(id),
tx.clone(),
gamepad,
&self.mappings,
))
}
}
/// Returns handle to gamepad with given ID. Unlike `connected_gamepad()`, this function will
/// also return handle to gamepad that is currently disconnected.
///
/// ```
/// # let mut gilrs = gilrs::Gilrs::new().unwrap();
/// use gilrs::{Button, EventType};
///
/// loop {
/// while let Some(ev) = gilrs.next_event() {
/// // unwrap() should never panic because we use id from event
/// let is_up_pressed = gilrs.gamepad(ev.id).is_pressed(Button::DPadUp);
///
/// match ev.event {
/// EventType::ButtonPressed(Button::South, _) if is_up_pressed => {
/// // do something…
/// }
/// _ => (),
/// }
/// }
/// # break;
/// }
/// ```
pub fn gamepad(&self, id: GamepadId) -> Gamepad {
Gamepad {
inner: self.inner.gamepad(id.0).unwrap(),
data: &self.gamepads_data[id.0],
}
}
/// Returns a reference to connected gamepad or `None`.
pub fn connected_gamepad(&self, id: GamepadId) -> Option<Gamepad<'_>> {
// Make sure that it will not panic even with invalid GamepadId, so ConnectedGamepadIterator
// will always work.
if let Some(data) = self.gamepads_data.get(id.0) {
let inner = self.inner.gamepad(id.0)?;
if inner.is_connected() {
Some(Gamepad { inner, data })
} else {
None
}
} else {
None
}
}
/// Returns iterator over all connected gamepads and their ids.
///
/// ```
/// # let gilrs = gilrs::Gilrs::new().unwrap();
/// for (id, gamepad) in gilrs.gamepads() {
/// assert!(gamepad.is_connected());
/// println!("Gamepad with id {} and name {} is connected",
/// id, gamepad.name());
/// }
/// ```
pub fn gamepads(&self) -> ConnectedGamepadsIterator<'_> {
ConnectedGamepadsIterator(self, 0)
}
/// Adds `ev` at the end of internal event queue. It can later be retrieved with `next_event()`.
pub fn insert_event(&mut self, ev: Event) {
self.events.push_back(ev);
}
pub(crate) fn ff_sender(&self) -> &Sender<Message> {
&self.tx
}
/// Sets gamepad's mapping and returns SDL2 representation of them. Returned mappings may not be
/// compatible with SDL2 - if it is important, use
/// [`set_mapping_strict()`](#method.set_mapping_strict).
///
/// The `name` argument can be a string slice with custom gamepad name or `None`. If `None`,
/// gamepad name reported by driver will be used.
///
/// # Errors
///
/// This function return error if `name` contains comma, `mapping` have axis and button entry
/// for same element (for example `Axis::LetfTrigger` and `Button::LeftTrigger`) or gamepad does
/// not have any element with `EvCode` used in mapping. `Button::Unknown` and
/// `Axis::Unknown` are not allowd as keys to `mapping` – in this case,
/// `MappingError::UnknownElement` is returned.
///
/// Error is also returned if this function is not implemented or gamepad is not connected.
///
/// # Example
///
/// ```
/// use gilrs::{Mapping, Button};
///
/// # let mut gilrs = gilrs::Gilrs::new().unwrap();
/// let mut data = Mapping::new();
/// // …
///
/// // or `match gilrs.set_mapping(0, &data, None) {`
/// match gilrs.set_mapping(0, &data, "Custom name") {
/// Ok(sdl) => println!("SDL2 mapping: {}", sdl),
/// Err(e) => println!("Failed to set mapping: {}", e),
/// };
/// ```
///
/// See also `examples/mapping.rs`.
pub fn set_mapping<'b, O: Into<Option<&'b str>>>(
&mut self,
gamepad_id: usize,
mapping: &MappingData,
name: O,
) -> Result<String, MappingError> {
if let Some(gamepad) = self.inner.gamepad(gamepad_id) {
if !gamepad.is_connected() {
return Err(MappingError::NotConnected);
}
let name = match name.into() {
Some(s) => s,
None => gamepad.name(),
};
let (mapping, s) = Mapping::from_data(
mapping,
gamepad.buttons(),
gamepad.axes(),
name,
Uuid::from_bytes(gamepad.uuid()),
)?;
// We checked if gamepad is connected, so it should never panic
let data = &mut self.gamepads_data[gamepad_id];
data.mapping = mapping;
Ok(s)
} else {
Err(MappingError::NotConnected)
}
}
/// Similar to [`set_mapping()`](#method.set_mapping) but returned string should be compatible
/// with SDL2.
///
/// # Errors
///
/// Returns `MappingError::NotSdl2Compatible` if `mapping` have an entry for `Button::{C, Z}`
/// or `Axis::{LeftZ, RightZ}`.
pub fn set_mapping_strict<'b, O: Into<Option<&'b str>>>(
&mut self,
gamepad_id: usize,
mapping: &MappingData,
name: O,
) -> Result<String, MappingError> {
if mapping.button(Button::C).is_some()
|| mapping.button(Button::Z).is_some()
|| mapping.axis(Axis::LeftZ).is_some()
|| mapping.axis(Axis::RightZ).is_some()
{
Err(MappingError::NotSdl2Compatible)
} else {
self.set_mapping(gamepad_id, mapping, name)
}
}
pub(crate) fn next_ff_id(&mut self) -> usize {
// TODO: reuse free ids
let id = self.next_id;
self.next_id = match self.next_id.checked_add(1) {
Some(x) => x,
None => panic!("Failed to assign ID to new effect"),
};
id
}
}
/// Allow to create `Gilrs ` with customized behaviour.
pub struct GilrsBuilder {
mappings: MappingDb,
default_filters: bool,
axis_to_btn_pressed: f32,
axis_to_btn_released: f32,
update_state: bool,
env_mappings: bool,
included_mappings: bool,
}
impl GilrsBuilder {
/// Create builder with default settings. Use `build()` to create `Gilrs`.
pub fn new() -> Self {
GilrsBuilder {
mappings: MappingDb::new(),
default_filters: true,
axis_to_btn_pressed: 0.75,
axis_to_btn_released: 0.65,
update_state: true,
env_mappings: true,
included_mappings: true,
}
}
/// If `true`, use [`axis_dpad_to_button`](ev/filter/fn.axis_dpad_to_button.html),
/// [`Jitter`](ev/filter/struct.Jitter.html) and [`deadzone`](ev/filter/fn.deadzone.html)
/// filters with default parameters. Defaults to `true`.
pub fn with_default_filters(mut self, default_filters: bool) -> Self {
self.default_filters = default_filters;
self
}
/// Adds SDL mappings.
pub fn add_mappings(mut self, mappings: &str) -> Self {
self.mappings.insert(mappings);
self
}
/// If true, will add SDL mappings from `SDL_GAMECONTROLLERCONFIG` environment variable.
/// Defaults to true.
pub fn add_env_mappings(mut self, env_mappings: bool) -> Self {
self.env_mappings = env_mappings;
self
}
/// If true, will add SDL mappings included from
/// https://github.com/gabomdq/SDL_GameControllerDB. Defaults to true.
pub fn add_included_mappings(mut self, included_mappings: bool) -> Self {
self.included_mappings = included_mappings;
self
}
/// Sets values on which `ButtonPressed` and `ButtonReleased` events will be emitted. `build()`
/// will return error if `pressed ≤ released` or if one of values is outside [0.0, 1.0].
///
/// Defaults to 0.75 for `pressed` and 0.65 for `released`.
pub fn set_axis_to_btn(mut self, pressed: f32, released: f32) -> Self {
self.axis_to_btn_pressed = pressed;
self.axis_to_btn_released = released;
self
}
/// Disable or enable automatic state updates. You should use this if you use custom filters;
/// in this case you have to update state manually anyway.
pub fn set_update_state(mut self, enabled: bool) -> Self {
self.update_state = enabled;
self
}
/// Creates `Gilrs`.
pub fn build(mut self) -> Result<Gilrs, Error> {
if self.included_mappings {
self.mappings.add_included_mappings();
}
if self.env_mappings {
self.mappings.add_env_mappings();
}
debug!("Loaded {} mappings.", self.mappings.len());
if self.axis_to_btn_pressed <= self.axis_to_btn_released
|| self.axis_to_btn_pressed < 0.0
|| self.axis_to_btn_pressed > 1.0
|| self.axis_to_btn_released < 0.0
|| self.axis_to_btn_released > 1.0
{
return Err(Error::InvalidAxisToBtn);
}
let mut is_dummy = false;
let inner = match gilrs_core::Gilrs::new() {
Ok(g) => g,
Err(PlatformError::NotImplemented(g)) => {
is_dummy = true;
g
}
Err(PlatformError::Other(e)) => return Err(Error::Other(e)),
Err(_) => unimplemented!(),
};
let (tx, rx) = server::init();
let mut gilrs = Gilrs {
inner,
next_id: 0,
tx,
rx,
counter: 0,
mappings: self.mappings,
default_filters: self.default_filters,
events: VecDeque::new(),
axis_to_btn_pressed: self.axis_to_btn_pressed,
axis_to_btn_released: self.axis_to_btn_released,
update_state: self.update_state,
gamepads_data: Vec::new(),
};
gilrs.finish_gamepads_creation();
if is_dummy {
Err(Error::NotImplemented(gilrs))
} else {
Ok(gilrs)
}
}
}
impl Default for GilrsBuilder {
fn default() -> Self {
Self::new()
}
}
/// Iterator over all connected gamepads.
pub struct ConnectedGamepadsIterator<'a>(&'a Gilrs, usize);
impl<'a> Iterator for ConnectedGamepadsIterator<'a> {
type Item = (GamepadId, Gamepad<'a>);
fn next(&mut self) -> Option<(GamepadId, Gamepad<'a>)> {
loop {
if self.1 == self.0.inner.last_gamepad_hint() {
return None;
}
if let Some(gp) = self.0.connected_gamepad(GamepadId(self.1)) {
let idx = self.1;
self.1 += 1;
return Some((GamepadId(idx), gp));
}
self.1 += 1;
}
}
}
/// Represents handle to game controller.
///
/// Using this struct you can access cached gamepad state, information about gamepad such as name
/// or UUID and manage force feedback effects.
#[derive(Debug, Copy, Clone)]
pub struct Gamepad<'a> {
data: &'a GamepadData,
inner: &'a gilrs_core::Gamepad,
}
impl Gamepad<'_> {
/// Returns the mapping name if it exists otherwise returns the os provided name.
pub fn name(&self) -> &str {
if let Some(map_name) = self.map_name() {
map_name
} else {
self.os_name()
}
}
/// if `mapping_source()` is `SdlMappings` returns the name of the mapping used by the gamepad.
/// Otherwise returns `None`.
pub fn map_name(&self) -> Option<&str> {
self.data.map_name()
}
/// Returns the name of the gamepad supplied by the OS.
pub fn os_name(&self) -> &str {
self.inner.name()
}
/// Returns gamepad's UUID.
///
/// It is recommended to process with the [UUID crate](https://crates.io/crates/uuid).
/// Use `Uuid::from_bytes` method to create a `Uuid` from the returned bytes.
pub fn uuid(&self) -> [u8; 16] {
self.inner.uuid()
}
/// Returns the vendor ID, as assigned by the USB-IF, when available.
pub fn vendor_id(&self) -> Option<u16> {
self.inner.vendor_id()
}
/// Returns the product ID, as assigned by the vendor, when available.
pub fn product_id(&self) -> Option<u16> {
self.inner.product_id()
}
/// Returns cached gamepad state.
pub fn state(&self) -> &GamepadState {
&self.data.state
}
/// Returns true if gamepad is connected.
pub fn is_connected(&self) -> bool {
self.inner.is_connected()
}
/// Examines cached gamepad state to check if given button is pressed. Panics if `btn` is
/// `Unknown`.
///
/// If you know `Code` of the element that you want to examine, it's recommended to use methods
/// directly on `State`, because this version have to check which `Code` is mapped to element of
/// gamepad.
pub fn is_pressed(&self, btn: Button) -> bool {
self.data.is_pressed(btn)
}
/// Examines cached gamepad state to check axis's value. Panics if `axis` is `Unknown`.
///
/// If you know `Code` of the element that you want to examine, it's recommended to use methods
/// directly on `State`, because this version have to check which `Code` is mapped to element of
/// gamepad.
pub fn value(&self, axis: Axis) -> f32 {
self.data.value(axis)
}
/// Returns button state and when it changed.
///
/// If you know `Code` of the element that you want to examine, it's recommended to use methods
/// directly on `State`, because this version have to check which `Code` is mapped to element of
/// gamepad.
pub fn button_data(&self, btn: Button) -> Option<&ButtonData> {
self.data.button_data(btn)
}
/// Returns axis state and when it changed.
///
/// If you know `Code` of the element that you want to examine, it's recommended to use methods
/// directly on `State`, because this version have to check which `Code` is mapped to element of
/// gamepad.
pub fn axis_data(&self, axis: Axis) -> Option<&AxisData> {
self.data.axis_data(axis)
}
/// Returns device's power supply state. See [`PowerInfo`](enum.PowerInfo.html) for details.
pub fn power_info(&self) -> PowerInfo {
self.inner.power_info()
}
/// Returns source of gamepad mapping. Can be used to filter gamepads which do not provide
/// unified controller layout.
///
/// ```
/// use gilrs::MappingSource;
/// # let mut gilrs = gilrs::Gilrs::new().unwrap();
///
/// for (_, gamepad) in gilrs.gamepads().filter(
/// |gp| gp.1.mapping_source() != MappingSource::None)
/// {
/// println!("{} is ready to use!", gamepad.name());
/// }
/// ```
pub fn mapping_source(&self) -> MappingSource {
if self.data.mapping.is_default() {
// TODO: check if it's Driver or None
MappingSource::Driver
} else {
MappingSource::SdlMappings
}
}
/// Returns true if force feedback is supported by device.
pub fn is_ff_supported(&self) -> bool {
self.inner.is_ff_supported()
}
/// Change gamepad position used by force feedback effects.
pub fn set_listener_position<Vec3: Into<[f32; 3]>>(
&self,
position: Vec3,
) -> Result<(), FfError> {
if !self.is_connected() {
Err(FfError::Disconnected(self.id()))
} else if !self.is_ff_supported() {
Err(FfError::FfNotSupported(self.id()))
} else {
self.data.tx.send(Message::SetListenerPosition {
id: self.data.id.0,
position: position.into(),
})?;
Ok(())
}
}
/// Returns `AxisOrBtn` mapped to `Code`.
pub fn axis_or_btn_name(&self, ec: Code) -> Option<AxisOrBtn> {
self.data.axis_or_btn_name(ec)
}
/// Returns `Code` associated with `btn`.
pub fn button_code(&self, btn: Button) -> Option<Code> {
self.data.button_code(btn)
}
/// Returns `Code` associated with `axis`.
pub fn axis_code(&self, axis: Axis) -> Option<Code> {
self.data.axis_code(axis)
}
/// Returns area in which axis events should be ignored.
pub fn deadzone(&self, axis: Code) -> Option<f32> {
self.inner.axis_info(axis.0).map(|i| {
let range = i.max as f32 - i.min as f32;
if range == 0.0 {
0.0
} else {
i.deadzone
.map(|d| d as f32 / range * 2.0)
.unwrap_or(DEFAULT_DEADZONE)
}
})
}
/// Returns ID of gamepad.
pub fn id(&self) -> GamepadId {
self.data.id
}
pub(crate) fn mapping(&self) -> &Mapping {
&self.data.mapping
}
}
#[derive(Debug)]
pub(crate) struct GamepadData {
state: GamepadState,
mapping: Mapping,
tx: Sender<Message>,
id: GamepadId,
// Flags used by the deadzone filter.
pub(crate) have_sent_nonzero_for_axis: [bool; 6],
}
impl GamepadData {
fn new(
id: GamepadId,
tx: Sender<Message>,
gamepad: &gilrs_core::Gamepad,
db: &MappingDb,
) -> Self {
let uuid = Uuid::from_bytes(gamepad.uuid());
let mapping = db
.get(uuid)
.map(
|s| match Mapping::parse_sdl_mapping(s, gamepad.buttons(), gamepad.axes()) {
Ok(result) => result,
Err(e) => {
warn!(
"Unable to parse SDL mapping for UUID {uuid}\n\t{e:?}\n\tDefault mapping \
will be used.",
);
Mapping::default(gamepad)
}
},
)
.unwrap_or_else(|| {
warn!("No mapping found for UUID {uuid}\n\tDefault mapping will be used.");
Mapping::default(gamepad)
});
if gamepad.is_ff_supported() && gamepad.is_connected() {
if let Some(device) = gamepad.ff_device() {
let _ = tx.send(Message::Open { id: id.0, device });
}
}
GamepadData {
state: GamepadState::new(),
mapping,
tx,
id,
have_sent_nonzero_for_axis: Default::default(),
}
}
/// if `mapping_source()` is `SdlMappings` returns the name of the mapping used by the gamepad.
/// Otherwise returns `None`.
///
/// Warning: Mappings are set after event `Connected` is processed, therefore this function will
/// always return `None` before first calls to `Gilrs::next_event()`.
pub fn map_name(&self) -> Option<&str> {
if self.mapping.is_default() {
None
} else {
Some(self.mapping.name())
}
}
/// Examines cached gamepad state to check if the given button is pressed. Panics if `btn` is
/// `Unknown`.
///
/// If you know `Code` of the element that you want to examine, it's recommended to use methods
/// directly on `State`, because this version has to check which `Code` is mapped to element of
/// gamepad.
pub fn is_pressed(&self, btn: Button) -> bool {
assert_ne!(btn, Button::Unknown);
self.button_code(btn)
.or_else(|| btn.to_nec())
.map(|nec| self.state.is_pressed(nec))
.unwrap_or(false)
}
/// Examines cached gamepad state to check axis's value. Panics if `axis` is `Unknown`.
///
/// If you know `Code` of the element that you want to examine, it's recommended to use methods
/// directly on `State`, because this version has to check which `Code` is mapped to element of
/// gamepad.
pub fn value(&self, axis: Axis) -> f32 {
assert_ne!(axis, Axis::Unknown);
self.axis_code(axis)
.map(|nec| self.state.value(nec))
.unwrap_or(0.0)
}
/// Returns button state and when it changed.
///
/// If you know `Code` of the element that you want to examine, it's recommended to use methods
/// directly on `State`, because this version has to check which `Code` is mapped to element of
/// gamepad.
pub fn button_data(&self, btn: Button) -> Option<&ButtonData> {
self.button_code(btn)
.and_then(|nec| self.state.button_data(nec))
}
/// Returns axis state and when it changed.
///
/// If you know `Code` of the element that you want to examine, it's recommended to use methods
/// directly on `State`, because this version has to check which `Code` is mapped to element of
/// gamepad.
pub fn axis_data(&self, axis: Axis) -> Option<&AxisData> {
self.axis_code(axis)
.and_then(|nec| self.state.axis_data(nec))
}
/// Returns `AxisOrBtn` mapped to `Code`.
pub fn axis_or_btn_name(&self, ec: Code) -> Option<AxisOrBtn> {
self.mapping.map(&ec.0)
}
/// Returns `Code` associated with `btn`.
pub fn button_code(&self, btn: Button) -> Option<Code> {
self.mapping.map_rev(&AxisOrBtn::Btn(btn)).map(Code)
}
/// Returns `Code` associated with `axis`.
pub fn axis_code(&self, axis: Axis) -> Option<Code> {
self.mapping.map_rev(&AxisOrBtn::Axis(axis)).map(Code)
}
}
/// Source of gamepad mappings.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum MappingSource {
/// Gamepad uses SDL mappings.
SdlMappings,
/// Gamepad does not use any mappings but driver should provide unified controller layout.
Driver,
/// Gamepad does not use any mappings and most gamepad events will probably be `Button::Unknown`
/// or `Axis::Unknown`
None,
}
/// Gamepad ID.
///
/// It's not possible to create an instance of this type directly, but you can obtain one from a Gamepad
/// handle or any event. ID is valid for the entire lifetime of the `Gilrs` context.
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
pub struct GamepadId(pub(crate) usize);
impl From<GamepadId> for usize {
fn from(x: GamepadId) -> usize {
x.0
}
}
impl Display for GamepadId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
fn axis_value(info: &AxisInfo, val: i32, axis: Axis) -> f32 {
let mut range = info.max as f32 - info.min as f32;
let mut val = val as f32 - info.min as f32;
if let Some(i_range) = info.max.checked_sub(info.min) {
// Only consider adjusting range & val if calculating the range doesn't cause overflow. If
// the range is so large overflow occurs, adjusting values by 1.0 would be insignificant.
if i_range % 2 == 1 {
// Add one to range and val, so the value is centered (like 127/255) will be mapped 0.0
range += 1.0;
val += 1.0;
}
}
val = val / range * 2.0 - 1.0;
if gilrs_core::IS_Y_AXIS_REVERSED
&& (axis == Axis::LeftStickY || axis == Axis::RightStickY || axis == Axis::DPadY)
&& val != 0.0
{
val = -val;
}
utils::clamp(val, -1.0, 1.0)
}
fn btn_value(info: &AxisInfo, val: i32) -> f32 {
let range = info.max as f32 - info.min as f32;
let mut val = val as f32 - info.min as f32;
val /= range;
utils::clamp(val, 0.0, 1.0)
}
/// Error type which can be returned when creating `Gilrs`.
#[non_exhaustive]
#[derive(Debug)]
pub enum Error {
/// Gilrs does not support the current platform, but you can use dummy context from this error if
/// gamepad input is not essential.
NotImplemented(Gilrs),
/// Either `pressed ≤ released` or one of values is outside [0.0, 1.0] range.
InvalidAxisToBtn,
/// Platform specific error.
Other(Box<dyn error::Error + Send + Sync + 'static>),
}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::NotImplemented(_) => f.write_str("Gilrs does not support current platform."),
Error::InvalidAxisToBtn => f.write_str(
"Either `pressed ≤ released` or one of values is outside [0.0, 1.0] range.",
),
Error::Other(ref e) => e.fmt(f),
}
}
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
Error::Other(e) => Some(e.as_ref()),
_ => None,
}
}
}
const _: () = {
const fn assert_send<T: Send>() {}
#[cfg(not(target_arch = "wasm32"))]
assert_send::<Gilrs>();
};
#[cfg(test)]
mod tests {
use super::{axis_value, btn_value, Axis, AxisInfo};
#[test]
fn axis_value_documented_case() {
let info = AxisInfo {
min: 0,
max: 255,
deadzone: None,
};
let axis = Axis::LeftStickY;
assert_eq!(0., axis_value(&info, 127, axis));
}
#[test]
fn axis_value_overflow() {
let info = AxisInfo {
min: i32::MIN,
max: i32::MAX,
deadzone: None,
};
let axis = Axis::LeftStickY;
assert_eq!(0., axis_value(&info, -1, axis));
assert_eq!(0., axis_value(&info, 0, axis));
assert_eq!(0., axis_value(&info, 1, axis));
assert_eq!(1.0, axis_value(&info, i32::MIN, axis));
assert_eq!(-1.0, axis_value(&info, i32::MAX, axis));
}
#[test]
fn btn_value_overflow() {
let info = AxisInfo {
min: i32::MIN,
max: i32::MAX,
deadzone: None,
};
assert_eq!(0.5, btn_value(&info, -1));
assert_eq!(0.5, btn_value(&info, 0));
assert_eq!(0.5, btn_value(&info, 1));
assert_eq!(0.0, btn_value(&info, i32::MIN));
assert_eq!(1.0, btn_value(&info, i32::MAX));
}
}
================================================
FILE: gilrs/src/lib.rs
================================================
// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
//! GilRs - Game Input Library for Rust
//! ===================================
//!
//! GilRs abstract platform specific APIs to provide unified interfaces for working with gamepads.
//!
//! Main features:
//!
//! - Unified gamepad layout—buttons and axes are represented by familiar names
//! - Support for SDL2 mappings including `SDL_GAMECONTROLLERCONFIG` environment
//! variable which Steam uses
//! - Hotplugging—GilRs will try to assign new IDs for new gamepads and reuse the same
//! ID for gamepads which reconnected
//! - Force feedback (rumble)
//! - Power information (is gamepad wired, current battery status)
//!
//! Example
//! -------
//!
//! ```rust
//! use gilrs::{Gilrs, Button, Event};
//!
//! let mut gilrs = Gilrs::new().unwrap();
//!
//! // Iterate over all connected gamepads
//! for (_id, gamepad) in gilrs.gamepads() {
//! println!("{} is {:?}", gamepad.name(), gamepad.power_info());
//! }
//!
//! let mut active_gamepad = None;
//!
//! loop {
//! // Examine new events
//! while let Some(Event { id, event, time, .. }) = gilrs.next_event() {
//! println!("{:?} New event from {}: {:?}", time, id, event);
//! active_gamepad = Some(id);
//! }
//!
//! // You can also use cached gamepad state
//! if let Some(gamepad) = active_gamepad.map(|id| gilrs.gamepad(id)) {
//! if gamepad.is_pressed(Button::South) {
//! println!("Button South is pressed (XBox - A, PS - X)");
//! }
//! }
//! # break;
//! }
//! ```
//!
//! Supported features
//! ------------------
//!
//! | | Input | Hotplugging | Force feedback |
//! |------------------|:-----:|:-----------:|:--------------:|
//! | Linux/BSD (evdev)| ✓ | ✓ | ✓ |
//! | Windows | ✓ | ✓ | ✓ |
//! | OS X | ✓ | ✓ | ✕ |
//! | Wasm | ✓ | ✓ | n/a |
//! | Android | ✕ | ✕ | ✕ |
//!
//! Controller layout
//! -----------------
//!
//! 
//! [original image by nicefrog](http://opengameart.org/content/generic-gamepad-template)
//!
//! Mappings
//! --------
//!
//! GilRs use SDL-compatible controller mappings to fix on Linux legacy drivers that doesn't follow
//! [Linux Gamepad API](https://www.kernel.org/doc/Documentation/input/gamepad.txt) and to provide
//! unified button layout for platforms that doesn't make any guarantees about it. The main source
//! is [SDL_GameControllerDB](https://github.com/gabomdq/SDL_GameControllerDB), but library also
//! support loading mappings from environment variable `SDL_GAMECONTROLLERCONFIG` (which Steam
//! use).
//!
//! Cargo features
//! --------------
//!
//! - `serde-serialize` - enable deriving of serde's `Serialize` and `Deserialize` for
//! various types.
//! - `wgi` - use Windows Gaming Input on Windows (enabled by default).
//! - `xinput` - use XInput on Windows.
//!
//! Platform specific notes
//! ======================
//!
//! Linux/BSD (evdev)
//! -----
//!
//! With evdev, GilRs read (and write, in case of force feedback) directly from appropriate
//! `/dev/input/event*` file. This mean that user have to have read and write access to this file.
//! On most distros it shouldn't be a problem, but if it is, you will have to create udev rule.
//! On FreeBSD generic HID gamepads use hgame(4) and special use Linux driver via `webcamd`.
//!
//! To build GilRs, you will need pkg-config and libudev .pc file. On some distributions this file
//! is packaged in separate archive (e.g., `libudev-dev` in Debian, `libudev-devd` in FreeBSD).
//!
//! Windows
//! -----
//!
//! Windows defaults to using Windows Gaming Input instead of XInput. If you need to use XInput you
//! can disable the `wgi` feature (it's enabled by default) and enable the `xinput` feature.
//!
//! Windows Gaming Input requires an in focus window to be associated with the process to receive
//! events. You can still switch back to using xInput by turning off default features and enabling
//! the xinput feature.
//!
//! Note: S
gitextract_3om1ehps/ ├── .gitignore ├── .gitlab-ci.yml ├── .gitmodules ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── appveyor.yml ├── gilrs/ │ ├── CHANGELOG.md │ ├── Cargo.toml │ ├── build.rs │ ├── examples/ │ │ ├── ev.rs │ │ ├── ff.rs │ │ ├── ff_pos.rs │ │ ├── gamepad_info.rs │ │ ├── gui.rs │ │ └── wasm/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── index.html │ │ ├── wasm_gui.ps1 │ │ └── wasm_gui.sh │ └── src/ │ ├── constants.rs │ ├── ev/ │ │ ├── filter.rs │ │ ├── mod.rs │ │ └── state.rs │ ├── ff/ │ │ ├── base_effect.rs │ │ ├── effect_source.rs │ │ ├── mod.rs │ │ ├── server.rs │ │ └── time.rs │ ├── gamepad.rs │ ├── lib.rs │ ├── mapping/ │ │ ├── mod.rs │ │ └── parser.rs │ └── utils.rs ├── gilrs-core/ │ ├── CHANGELOG.md │ ├── Cargo.toml │ ├── README.md │ ├── examples/ │ │ └── ev_core.rs │ └── src/ │ ├── lib.rs │ ├── platform/ │ │ ├── default/ │ │ │ ├── ff.rs │ │ │ ├── gamepad.rs │ │ │ └── mod.rs │ │ ├── linux/ │ │ │ ├── ff.rs │ │ │ ├── gamepad.rs │ │ │ ├── ioctl.rs │ │ │ ├── mod.rs │ │ │ └── udev.rs │ │ ├── macos/ │ │ │ ├── ff.rs │ │ │ ├── gamepad.rs │ │ │ ├── io_kit.rs │ │ │ └── mod.rs │ │ ├── mod.rs │ │ ├── wasm/ │ │ │ ├── ff.rs │ │ │ ├── gamepad.rs │ │ │ └── mod.rs │ │ ├── windows_wgi/ │ │ │ ├── ff.rs │ │ │ ├── gamepad.rs │ │ │ └── mod.rs │ │ └── windows_xinput/ │ │ ├── ff.rs │ │ ├── gamepad.rs │ │ └── mod.rs │ └── utils.rs └── rustfmt.toml
SYMBOL INDEX (1018 symbols across 43 files)
FILE: gilrs-core/examples/ev_core.rs
function main (line 3) | fn main() {
FILE: gilrs-core/src/lib.rs
constant IS_Y_AXIS_REVERSED (line 16) | pub const IS_Y_AXIS_REVERSED: bool = platform::IS_Y_AXIS_REVERSED;
type FfDevice (line 20) | pub struct FfDevice {
method set_ff_state (line 26) | pub fn set_ff_state(&mut self, strong: u16, weak: u16, min_duration: D...
type Event (line 34) | pub struct Event {
method new (line 45) | pub fn new(id: usize, event: EventType) -> Self {
type EventType (line 54) | pub enum EventType {
type AxisInfo (line 64) | pub struct AxisInfo {
type PowerInfo (line 87) | pub enum PowerInfo {
type Gilrs (line 102) | pub struct Gilrs {
method new (line 107) | pub fn new() -> Result<Self, Error> {
method next_event (line 117) | pub fn next_event(&mut self) -> Option<Event> {
method next_event_blocking (line 122) | pub fn next_event_blocking(&mut self, timeout: Option<Duration>) -> Op...
method gamepad (line 127) | pub fn gamepad(&self, id: usize) -> Option<&Gamepad> {
method last_gamepad_hint (line 141) | pub fn last_gamepad_hint(&self) -> usize {
type Gamepad (line 149) | pub struct Gamepad {
method name (line 155) | pub fn name(&self) -> &str {
method is_connected (line 160) | pub fn is_connected(&self) -> bool {
method uuid (line 171) | pub fn uuid(&self) -> [u8; 16] {
method vendor_id (line 176) | pub fn vendor_id(&self) -> Option<u16> {
method product_id (line 181) | pub fn product_id(&self) -> Option<u16> {
method power_info (line 186) | pub fn power_info(&self) -> PowerInfo {
method is_ff_supported (line 191) | pub fn is_ff_supported(&self) -> bool {
method ff_device (line 196) | pub fn ff_device(&self) -> Option<FfDevice> {
method buttons (line 201) | pub fn buttons(&self) -> &[EvCode] {
method axes (line 210) | pub fn axes(&self) -> &[EvCode] {
method axis_info (line 220) | pub fn axis_info(&self, nec: EvCode) -> Option<&AxisInfo> {
type EvCode (line 232) | pub struct EvCode(platform::EvCode);
method into_u32 (line 235) | pub fn into_u32(self) -> u32 {
method fmt (line 241) | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
type PlatformError (line 250) | enum PlatformError {
method source (line 272) | fn source(&self) -> Option<&(dyn error::Error + 'static)> {
method fmt (line 261) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
type Error (line 283) | pub enum Error {
method source (line 301) | fn source(&self) -> Option<&(dyn error::Error + 'static)> {
method fmt (line 292) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
constant AXIS_LSTICKX (line 315) | pub const AXIS_LSTICKX: EvCode = EvCode(nec::AXIS_LSTICKX);
constant AXIS_LSTICKY (line 316) | pub const AXIS_LSTICKY: EvCode = EvCode(nec::AXIS_LSTICKY);
constant AXIS_LEFTZ (line 317) | pub const AXIS_LEFTZ: EvCode = EvCode(nec::AXIS_LEFTZ);
constant AXIS_RSTICKX (line 318) | pub const AXIS_RSTICKX: EvCode = EvCode(nec::AXIS_RSTICKX);
constant AXIS_RSTICKY (line 319) | pub const AXIS_RSTICKY: EvCode = EvCode(nec::AXIS_RSTICKY);
constant AXIS_RIGHTZ (line 320) | pub const AXIS_RIGHTZ: EvCode = EvCode(nec::AXIS_RIGHTZ);
constant AXIS_DPADX (line 321) | pub const AXIS_DPADX: EvCode = EvCode(nec::AXIS_DPADX);
constant AXIS_DPADY (line 322) | pub const AXIS_DPADY: EvCode = EvCode(nec::AXIS_DPADY);
constant AXIS_RT (line 323) | pub const AXIS_RT: EvCode = EvCode(nec::AXIS_RT);
constant AXIS_LT (line 324) | pub const AXIS_LT: EvCode = EvCode(nec::AXIS_LT);
constant AXIS_RT2 (line 325) | pub const AXIS_RT2: EvCode = EvCode(nec::AXIS_RT2);
constant AXIS_LT2 (line 326) | pub const AXIS_LT2: EvCode = EvCode(nec::AXIS_LT2);
constant BTN_SOUTH (line 328) | pub const BTN_SOUTH: EvCode = EvCode(nec::BTN_SOUTH);
constant BTN_EAST (line 329) | pub const BTN_EAST: EvCode = EvCode(nec::BTN_EAST);
constant BTN_C (line 330) | pub const BTN_C: EvCode = EvCode(nec::BTN_C);
constant BTN_NORTH (line 331) | pub const BTN_NORTH: EvCode = EvCode(nec::BTN_NORTH);
constant BTN_WEST (line 332) | pub const BTN_WEST: EvCode = EvCode(nec::BTN_WEST);
constant BTN_Z (line 333) | pub const BTN_Z: EvCode = EvCode(nec::BTN_Z);
constant BTN_LT (line 334) | pub const BTN_LT: EvCode = EvCode(nec::BTN_LT);
constant BTN_RT (line 335) | pub const BTN_RT: EvCode = EvCode(nec::BTN_RT);
constant BTN_LT2 (line 336) | pub const BTN_LT2: EvCode = EvCode(nec::BTN_LT2);
constant BTN_RT2 (line 337) | pub const BTN_RT2: EvCode = EvCode(nec::BTN_RT2);
constant BTN_SELECT (line 338) | pub const BTN_SELECT: EvCode = EvCode(nec::BTN_SELECT);
constant BTN_START (line 339) | pub const BTN_START: EvCode = EvCode(nec::BTN_START);
constant BTN_MODE (line 340) | pub const BTN_MODE: EvCode = EvCode(nec::BTN_MODE);
constant BTN_LTHUMB (line 341) | pub const BTN_LTHUMB: EvCode = EvCode(nec::BTN_LTHUMB);
constant BTN_RTHUMB (line 342) | pub const BTN_RTHUMB: EvCode = EvCode(nec::BTN_RTHUMB);
constant BTN_DPAD_UP (line 344) | pub const BTN_DPAD_UP: EvCode = EvCode(nec::BTN_DPAD_UP);
constant BTN_DPAD_DOWN (line 345) | pub const BTN_DPAD_DOWN: EvCode = EvCode(nec::BTN_DPAD_DOWN);
constant BTN_DPAD_LEFT (line 346) | pub const BTN_DPAD_LEFT: EvCode = EvCode(nec::BTN_DPAD_LEFT);
constant BTN_DPAD_RIGHT (line 347) | pub const BTN_DPAD_RIGHT: EvCode = EvCode(nec::BTN_DPAD_RIGHT);
FILE: gilrs-core/src/platform/default/ff.rs
type Device (line 12) | pub struct Device;
method set_ff_state (line 16) | pub fn set_ff_state(&mut self, strong: u16, weak: u16, min_duration: D...
FILE: gilrs-core/src/platform/default/gamepad.rs
type Gilrs (line 17) | pub struct Gilrs {}
method new (line 20) | pub(crate) fn new() -> Result<Self, PlatformError> {
method next_event (line 24) | pub(crate) fn next_event(&mut self) -> Option<Event> {
method next_event_blocking (line 28) | pub(crate) fn next_event_blocking(&mut self, timeout: Option<Duration>...
method gamepad (line 32) | pub fn gamepad(&self, id: usize) -> Option<&Gamepad> {
method last_gamepad_hint (line 37) | pub fn last_gamepad_hint(&self) -> usize {
type Gamepad (line 43) | pub struct Gamepad {
method name (line 48) | pub fn name(&self) -> &str {
method uuid (line 52) | pub fn uuid(&self) -> Uuid {
method vendor_id (line 56) | pub fn vendor_id(&self) -> Option<u16> {
method product_id (line 60) | pub fn product_id(&self) -> Option<u16> {
method power_info (line 64) | pub fn power_info(&self) -> PowerInfo {
method is_ff_supported (line 68) | pub fn is_ff_supported(&self) -> bool {
method ff_device (line 73) | pub fn ff_device(&self) -> Option<FfDevice> {
method buttons (line 77) | pub fn buttons(&self) -> &[EvCode] {
method axes (line 81) | pub fn axes(&self) -> &[EvCode] {
method axis_info (line 85) | pub(crate) fn axis_info(&self, nec: EvCode) -> Option<&AxisInfo> {
method is_connected (line 89) | pub fn is_connected(&self) -> bool {
type EvCode (line 99) | pub struct EvCode(u16);
method into_u32 (line 102) | pub fn into_u32(self) -> u32 {
method fmt (line 108) | fn fmt(&self, f: &mut Formatter) -> FmtResult {
constant AXIS_LSTICKX (line 116) | pub const AXIS_LSTICKX: EvCode = EvCode(0);
constant AXIS_LSTICKY (line 117) | pub const AXIS_LSTICKY: EvCode = EvCode(1);
constant AXIS_LEFTZ (line 118) | pub const AXIS_LEFTZ: EvCode = EvCode(2);
constant AXIS_RSTICKX (line 119) | pub const AXIS_RSTICKX: EvCode = EvCode(3);
constant AXIS_RSTICKY (line 120) | pub const AXIS_RSTICKY: EvCode = EvCode(4);
constant AXIS_RIGHTZ (line 121) | pub const AXIS_RIGHTZ: EvCode = EvCode(5);
constant AXIS_DPADX (line 122) | pub const AXIS_DPADX: EvCode = EvCode(6);
constant AXIS_DPADY (line 123) | pub const AXIS_DPADY: EvCode = EvCode(7);
constant AXIS_RT (line 124) | pub const AXIS_RT: EvCode = EvCode(8);
constant AXIS_LT (line 125) | pub const AXIS_LT: EvCode = EvCode(9);
constant AXIS_RT2 (line 126) | pub const AXIS_RT2: EvCode = EvCode(10);
constant AXIS_LT2 (line 127) | pub const AXIS_LT2: EvCode = EvCode(11);
constant BTN_SOUTH (line 129) | pub const BTN_SOUTH: EvCode = EvCode(12);
constant BTN_EAST (line 130) | pub const BTN_EAST: EvCode = EvCode(13);
constant BTN_C (line 131) | pub const BTN_C: EvCode = EvCode(14);
constant BTN_NORTH (line 132) | pub const BTN_NORTH: EvCode = EvCode(15);
constant BTN_WEST (line 133) | pub const BTN_WEST: EvCode = EvCode(16);
constant BTN_Z (line 134) | pub const BTN_Z: EvCode = EvCode(17);
constant BTN_LT (line 135) | pub const BTN_LT: EvCode = EvCode(18);
constant BTN_RT (line 136) | pub const BTN_RT: EvCode = EvCode(19);
constant BTN_LT2 (line 137) | pub const BTN_LT2: EvCode = EvCode(20);
constant BTN_RT2 (line 138) | pub const BTN_RT2: EvCode = EvCode(21);
constant BTN_SELECT (line 139) | pub const BTN_SELECT: EvCode = EvCode(22);
constant BTN_START (line 140) | pub const BTN_START: EvCode = EvCode(23);
constant BTN_MODE (line 141) | pub const BTN_MODE: EvCode = EvCode(24);
constant BTN_LTHUMB (line 142) | pub const BTN_LTHUMB: EvCode = EvCode(25);
constant BTN_RTHUMB (line 143) | pub const BTN_RTHUMB: EvCode = EvCode(26);
constant BTN_DPAD_UP (line 145) | pub const BTN_DPAD_UP: EvCode = EvCode(27);
constant BTN_DPAD_DOWN (line 146) | pub const BTN_DPAD_DOWN: EvCode = EvCode(28);
constant BTN_DPAD_LEFT (line 147) | pub const BTN_DPAD_LEFT: EvCode = EvCode(29);
constant BTN_DPAD_RIGHT (line 148) | pub const BTN_DPAD_RIGHT: EvCode = EvCode(30);
FILE: gilrs-core/src/platform/default/mod.rs
constant IS_Y_AXIS_REVERSED (line 14) | pub const IS_Y_AXIS_REVERSED: bool = false;
FILE: gilrs-core/src/platform/linux/ff.rs
type Device (line 18) | pub struct Device {
method new (line 24) | pub(crate) fn new(path: &str) -> IoResult<Self> {
method set_ff_state (line 48) | pub fn set_ff_state(&mut self, strong: u16, weak: u16, min_duration: D...
method drop (line 106) | fn drop(&mut self) {
constant EV_FF (line 123) | const EV_FF: u16 = 0x15;
constant FF_RUMBLE (line 124) | const FF_RUMBLE: u16 = 0x50;
FILE: gilrs-core/src/platform/linux/gamepad.rs
constant HOTPLUG_DATA (line 41) | const HOTPLUG_DATA: u64 = u64::MAX;
type Gilrs (line 44) | pub struct Gilrs {
method new (line 61) | pub(crate) fn new() -> Result<Self, PlatformError> {
method next_event (line 208) | pub(crate) fn next_event(&mut self) -> Option<Event> {
method next_event_blocking (line 212) | pub(crate) fn next_event_blocking(&mut self, timeout: Option<Duration>...
method next_event_impl (line 216) | fn next_event_impl(&mut self, timeout: Option<Duration>) -> Option<Eve...
method gamepad (line 289) | pub fn gamepad(&self, id: usize) -> Option<&Gamepad> {
method last_gamepad_hint (line 293) | pub fn last_gamepad_hint(&self) -> usize {
method handle_hotplug (line 297) | fn handle_hotplug(&mut self) -> Option<Event> {
type DiscoveryBackend (line 53) | enum DiscoveryBackend {
constant INPUT_DIR_PATH (line 58) | const INPUT_DIR_PATH: &str = "/dev/input";
type HotplugEvent (line 357) | enum HotplugEvent {
function handle_inotify (line 362) | fn handle_inotify(
function get_gamepad_path (line 413) | fn get_gamepad_path(name: &str) -> Option<(PathBuf, PathBuf)> {
function handle_hotplug (line 429) | fn handle_hotplug(sender: Sender<HotplugEvent>, monitor: Monitor, event:...
type AxesInfo (line 495) | struct AxesInfo {
method new (line 500) | fn new(fd: i32) -> Self {
type Output (line 531) | type Output = AxisInfo;
method index (line 533) | fn index(&self, i: u16) -> &Self::Output {
type Gamepad (line 539) | pub struct Gamepad {
method open (line 559) | fn open(path: &CStr, syspath: &Path, discovery_backend: DiscoveryBacke...
method register_fd (line 645) | fn register_fd(&self, epoll: &Epoll, data: u64) -> Result<(), Errno> {
method collect_axes_and_buttons (line 650) | fn collect_axes_and_buttons(&mut self) {
method get_name (line 673) | fn get_name(fd: i32) -> Option<String> {
method get_input_id (line 688) | fn get_input_id(fd: i32) -> Option<ioctl::input_id> {
method test_ff (line 699) | fn test_ff(fd: i32) -> bool {
method is_gamepad (line 719) | fn is_gamepad(&self) -> bool {
method find_buttons (line 724) | fn find_buttons(key_bits: &[u8], only_gamepad_btns: bool) -> Vec<EvCod...
method find_axes (line 754) | fn find_axes(abs_bits: &[u8]) -> Vec<EvCode> {
method battery_fd (line 766) | fn battery_fd(syspath: &Path) -> (i32, i32) {
method event (line 785) | fn event(&mut self) -> Option<(EventType, SystemTime)> {
method next_event (line 832) | fn next_event(&mut self) -> Option<input_event> {
method compare_state (line 865) | fn compare_state(&mut self) {
method disconnect (line 913) | fn disconnect(&mut self) {
method is_connected (line 924) | pub fn is_connected(&self) -> bool {
method power_info (line 928) | pub fn power_info(&self) -> PowerInfo {
method is_ff_supported (line 982) | pub fn is_ff_supported(&self) -> bool {
method name (line 986) | pub fn name(&self) -> &str {
method uuid (line 990) | pub fn uuid(&self) -> Uuid {
method vendor_id (line 994) | pub fn vendor_id(&self) -> Option<u16> {
method product_id (line 998) | pub fn product_id(&self) -> Option<u16> {
method ff_device (line 1002) | pub fn ff_device(&self) -> Option<FfDevice> {
method buttons (line 1010) | pub fn buttons(&self) -> &[EvCode] {
method axes (line 1014) | pub fn axes(&self) -> &[EvCode] {
method axis_info (line 1018) | pub(crate) fn axis_info(&self, nec: EvCode) -> Option<&AxisInfo> {
method drop (line 1028) | fn drop(&mut self) {
method eq (line 1044) | fn eq(&self, other: &Self) -> bool {
function create_uuid (line 1049) | fn create_uuid(iid: ioctl::input_id) -> Uuid {
function cstr_new (line 1071) | unsafe fn cstr_new(bytes: &[u8]) -> &CStr {
type EvCode (line 1080) | pub struct EvCode {
method new (line 1086) | fn new(kind: u16, code: u16) -> Self {
method into_u32 (line 1090) | pub fn into_u32(self) -> u32 {
function from (line 1096) | fn from(f: input_event) -> Self {
method fmt (line 1105) | fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
type Error (line 1122) | enum Error {
method fmt (line 1129) | fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
function errno_to_platform_error (line 1140) | fn errno_to_platform_error(errno: Errno, ctx: &'static str) -> PlatformE...
constant KEY_MAX (line 1144) | const KEY_MAX: u16 = 0x2ff;
constant EV_MAX (line 1146) | const EV_MAX: u16 = 0x1f;
constant EV_SYN (line 1147) | const EV_SYN: u16 = 0x00;
constant EV_KEY (line 1148) | const EV_KEY: u16 = 0x01;
constant EV_REL (line 1149) | const EV_REL: u16 = 0x02;
constant EV_ABS (line 1150) | const EV_ABS: u16 = 0x03;
constant EV_MSC (line 1151) | const EV_MSC: u16 = 0x04;
constant EV_SW (line 1152) | const EV_SW: u16 = 0x05;
constant ABS_MAX (line 1153) | const ABS_MAX: u16 = 0x3f;
constant EV_FF (line 1154) | const EV_FF: u16 = 0x15;
constant SYN_REPORT (line 1156) | const SYN_REPORT: u16 = 0x00;
constant SYN_DROPPED (line 1157) | const SYN_DROPPED: u16 = 0x03;
constant BTN_MISC (line 1159) | const BTN_MISC: u16 = 0x100;
constant BTN_MOUSE (line 1160) | const BTN_MOUSE: u16 = 0x110;
constant BTN_JOYSTICK (line 1161) | const BTN_JOYSTICK: u16 = 0x120;
constant BTN_SOUTH (line 1162) | const BTN_SOUTH: u16 = 0x130;
constant BTN_EAST (line 1163) | const BTN_EAST: u16 = 0x131;
constant BTN_C (line 1165) | const BTN_C: u16 = 0x132;
constant BTN_NORTH (line 1166) | const BTN_NORTH: u16 = 0x133;
constant BTN_WEST (line 1167) | const BTN_WEST: u16 = 0x134;
constant BTN_Z (line 1169) | const BTN_Z: u16 = 0x135;
constant BTN_TL (line 1170) | const BTN_TL: u16 = 0x136;
constant BTN_TR (line 1171) | const BTN_TR: u16 = 0x137;
constant BTN_TL2 (line 1172) | const BTN_TL2: u16 = 0x138;
constant BTN_TR2 (line 1173) | const BTN_TR2: u16 = 0x139;
constant BTN_SELECT (line 1174) | const BTN_SELECT: u16 = 0x13a;
constant BTN_START (line 1175) | const BTN_START: u16 = 0x13b;
constant BTN_MODE (line 1176) | const BTN_MODE: u16 = 0x13c;
constant BTN_THUMBL (line 1177) | const BTN_THUMBL: u16 = 0x13d;
constant BTN_THUMBR (line 1178) | const BTN_THUMBR: u16 = 0x13e;
constant BTN_DPAD_UP (line 1180) | const BTN_DPAD_UP: u16 = 0x220;
constant BTN_DPAD_DOWN (line 1181) | const BTN_DPAD_DOWN: u16 = 0x221;
constant BTN_DPAD_LEFT (line 1182) | const BTN_DPAD_LEFT: u16 = 0x222;
constant BTN_DPAD_RIGHT (line 1183) | const BTN_DPAD_RIGHT: u16 = 0x223;
constant ABS_X (line 1185) | const ABS_X: u16 = 0x00;
constant ABS_Y (line 1186) | const ABS_Y: u16 = 0x01;
constant ABS_Z (line 1187) | const ABS_Z: u16 = 0x02;
constant ABS_RX (line 1188) | const ABS_RX: u16 = 0x03;
constant ABS_RY (line 1189) | const ABS_RY: u16 = 0x04;
constant ABS_RZ (line 1190) | const ABS_RZ: u16 = 0x05;
constant ABS_HAT0X (line 1191) | const ABS_HAT0X: u16 = 0x10;
constant ABS_HAT0Y (line 1192) | const ABS_HAT0Y: u16 = 0x11;
constant ABS_HAT1X (line 1193) | const ABS_HAT1X: u16 = 0x12;
constant ABS_HAT1Y (line 1194) | const ABS_HAT1Y: u16 = 0x13;
constant ABS_HAT2X (line 1195) | const ABS_HAT2X: u16 = 0x14;
constant ABS_HAT2Y (line 1196) | const ABS_HAT2Y: u16 = 0x15;
constant FF_MAX (line 1198) | const FF_MAX: u16 = FF_GAIN;
constant FF_SQUARE (line 1199) | const FF_SQUARE: u16 = 0x58;
constant FF_TRIANGLE (line 1200) | const FF_TRIANGLE: u16 = 0x59;
constant FF_SINE (line 1201) | const FF_SINE: u16 = 0x5a;
constant FF_GAIN (line 1202) | const FF_GAIN: u16 = 0x60;
constant BTN_SOUTH (line 1207) | pub const BTN_SOUTH: EvCode = EvCode {
constant BTN_EAST (line 1211) | pub const BTN_EAST: EvCode = EvCode {
constant BTN_C (line 1215) | pub const BTN_C: EvCode = EvCode {
constant BTN_NORTH (line 1219) | pub const BTN_NORTH: EvCode = EvCode {
constant BTN_WEST (line 1223) | pub const BTN_WEST: EvCode = EvCode {
constant BTN_Z (line 1227) | pub const BTN_Z: EvCode = EvCode {
constant BTN_LT (line 1231) | pub const BTN_LT: EvCode = EvCode {
constant BTN_RT (line 1235) | pub const BTN_RT: EvCode = EvCode {
constant BTN_LT2 (line 1239) | pub const BTN_LT2: EvCode = EvCode {
constant BTN_RT2 (line 1243) | pub const BTN_RT2: EvCode = EvCode {
constant BTN_SELECT (line 1247) | pub const BTN_SELECT: EvCode = EvCode {
constant BTN_START (line 1251) | pub const BTN_START: EvCode = EvCode {
constant BTN_MODE (line 1255) | pub const BTN_MODE: EvCode = EvCode {
constant BTN_LTHUMB (line 1259) | pub const BTN_LTHUMB: EvCode = EvCode {
constant BTN_RTHUMB (line 1263) | pub const BTN_RTHUMB: EvCode = EvCode {
constant BTN_DPAD_UP (line 1267) | pub const BTN_DPAD_UP: EvCode = EvCode {
constant BTN_DPAD_DOWN (line 1271) | pub const BTN_DPAD_DOWN: EvCode = EvCode {
constant BTN_DPAD_LEFT (line 1275) | pub const BTN_DPAD_LEFT: EvCode = EvCode {
constant BTN_DPAD_RIGHT (line 1279) | pub const BTN_DPAD_RIGHT: EvCode = EvCode {
constant AXIS_LSTICKX (line 1284) | pub const AXIS_LSTICKX: EvCode = EvCode {
constant AXIS_LSTICKY (line 1288) | pub const AXIS_LSTICKY: EvCode = EvCode {
constant AXIS_LEFTZ (line 1292) | pub const AXIS_LEFTZ: EvCode = EvCode {
constant AXIS_RSTICKX (line 1296) | pub const AXIS_RSTICKX: EvCode = EvCode {
constant AXIS_RSTICKY (line 1300) | pub const AXIS_RSTICKY: EvCode = EvCode {
constant AXIS_RIGHTZ (line 1304) | pub const AXIS_RIGHTZ: EvCode = EvCode {
constant AXIS_DPADX (line 1308) | pub const AXIS_DPADX: EvCode = EvCode {
constant AXIS_DPADY (line 1312) | pub const AXIS_DPADY: EvCode = EvCode {
constant AXIS_RT (line 1316) | pub const AXIS_RT: EvCode = EvCode {
constant AXIS_LT (line 1320) | pub const AXIS_LT: EvCode = EvCode {
constant AXIS_RT2 (line 1324) | pub const AXIS_RT2: EvCode = EvCode {
constant AXIS_LT2 (line 1328) | pub const AXIS_LT2: EvCode = EvCode {
function sdl_uuid (line 1341) | fn sdl_uuid() {
FILE: gilrs-core/src/platform/linux/ioctl.rs
type IoctlRequest (line 17) | pub type IoctlRequest = libc::c_int;
type IoctlRequest (line 19) | pub type IoctlRequest = libc::c_ulong;
function eviocgbit (line 27) | pub unsafe fn eviocgbit(fd: libc::c_int, ev: u32, len: libc::c_int, buf:...
function eviocgabs (line 35) | pub unsafe fn eviocgabs(fd: ::libc::c_int, abs: u32, buf: *mut input_abs...
type input_event (line 46) | pub struct input_event {
method default (line 54) | fn default() -> Self {
method fmt (line 60) | fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
type input_id (line 71) | pub struct input_id {
type input_absinfo (line 80) | pub struct input_absinfo {
type ff_replay (line 91) | pub struct ff_replay {
type ff_trigger (line 98) | pub struct ff_trigger {
type ff_envelope (line 105) | pub struct ff_envelope {
type ff_constant_effect (line 114) | pub struct ff_constant_effect {
type ff_ramp_effect (line 121) | pub struct ff_ramp_effect {
type ff_condition_effect (line 129) | pub struct ff_condition_effect {
type ff_periodic_effect (line 142) | pub struct ff_periodic_effect {
type ff_rumble_effect (line 157) | pub struct ff_rumble_effect {
type ff_effect (line 164) | pub struct ff_effect {
FILE: gilrs-core/src/platform/linux/mod.rs
constant IS_Y_AXIS_REVERSED (line 16) | pub const IS_Y_AXIS_REVERSED: bool = true;
FILE: gilrs-core/src/platform/linux/udev.rs
type Udev (line 15) | pub struct Udev(*mut ud::udev);
method new (line 18) | pub fn new() -> Option<Self> {
method enumerate (line 27) | pub fn enumerate(&self) -> Option<Enumerate> {
method drop (line 39) | fn drop(&mut self) {
method clone (line 47) | fn clone(&self) -> Self {
type Enumerate (line 52) | pub struct Enumerate(*mut ud::udev_enumerate);
method scan_devices (line 55) | pub fn scan_devices(&self) {
method add_match_property (line 60) | pub fn add_match_property(&self, key: &CStr, val: &CStr) {
method add_match_subsystem (line 67) | pub fn add_match_subsystem(&self, subsystem: &CStr) {
method iter (line 74) | pub fn iter(&self) -> DeviceIterator {
method drop (line 80) | fn drop(&mut self) {
type DeviceIterator (line 87) | pub struct DeviceIterator(*mut ud::udev_list_entry);
type Item (line 90) | type Item = CString;
method next (line 92) | fn next(&mut self) -> Option<CString> {
type Device (line 108) | pub struct Device(*mut ud::udev_device);
method from_syspath (line 111) | pub fn from_syspath(udev: &Udev, path: &CStr) -> Option<Self> {
method syspath (line 120) | pub fn syspath(&self) -> &CStr {
method devnode (line 125) | pub fn devnode(&self) -> Option<&CStr> {
method properties (line 137) | pub fn properties(&self) -> PropertyIterator {
method action (line 142) | pub fn action(&self) -> Option<&CStr> {
method property_value (line 153) | pub fn property_value(&self, key: &CStr) -> Option<&CStr> {
method clone (line 166) | fn clone(&self) -> Self {
method drop (line 172) | fn drop(&mut self) {
type PropertyIterator (line 180) | pub struct PropertyIterator(*mut ud::udev_list_entry);
type Item (line 183) | type Item = (String, String);
method next (line 185) | fn next(&mut self) -> Option<(String, String)> {
type Monitor (line 211) | pub struct Monitor(*mut ud::udev_monitor);
method new (line 214) | pub fn new(udev: &Udev) -> Option<Self> {
method wait_hotplug_available (line 232) | pub fn wait_hotplug_available(&self) -> bool {
method device (line 243) | pub fn device(&self) -> Device {
method drop (line 249) | fn drop(&mut self) {
FILE: gilrs-core/src/platform/macos/ff.rs
type Device (line 12) | pub struct Device;
method set_ff_state (line 16) | pub fn set_ff_state(&mut self, _strong: u16, _weak: u16, _min_duration...
FILE: gilrs-core/src/platform/macos/gamepad.rs
type Gilrs (line 30) | pub struct Gilrs {
method new (line 37) | pub(crate) fn new() -> Result<Self, PlatformError> {
method spawn_thread (line 51) | fn spawn_thread(
method next_event (line 96) | pub(crate) fn next_event(&mut self) -> Option<Event> {
method next_event_blocking (line 101) | pub(crate) fn next_event_blocking(&mut self, timeout: Option<Duration>...
method handle_event (line 111) | fn handle_event(&mut self, event: Option<(Event, Option<Device>)>) -> ...
method gamepad (line 156) | pub fn gamepad(&self, id: usize) -> Option<&Gamepad> {
method last_gamepad_hint (line 161) | pub fn last_gamepad_hint(&self) -> usize {
type Gamepad (line 168) | pub struct Gamepad {
method open (line 185) | fn open(device: &IOHIDDevice) -> Option<Gamepad> {
method create_uuid (line 275) | fn create_uuid(device: &IOHIDDevice) -> Option<Uuid> {
method name (line 324) | pub fn name(&self) -> &str {
method vendor_id (line 328) | pub fn vendor_id(&self) -> Option<u16> {
method product_id (line 332) | pub fn product_id(&self) -> Option<u16> {
method uuid (line 336) | pub fn uuid(&self) -> Uuid {
method power_info (line 340) | pub fn power_info(&self) -> PowerInfo {
method is_ff_supported (line 344) | pub fn is_ff_supported(&self) -> bool {
method ff_device (line 349) | pub fn ff_device(&self) -> Option<FfDevice> {
method buttons (line 353) | pub fn buttons(&self) -> &[EvCode] {
method axes (line 357) | pub fn axes(&self) -> &[EvCode] {
method axis_info (line 361) | pub(crate) fn axis_info(&self, nec: EvCode) -> Option<&AxisInfo> {
method is_connected (line 365) | pub fn is_connected(&self) -> bool {
method collect_axes_and_buttons (line 369) | fn collect_axes_and_buttons(&mut self, elements: &Vec<CFRetained<IOHID...
method collect_axes (line 383) | fn collect_axes(&mut self, elements: &Vec<CFRetained<IOHIDElement>>, c...
method collect_buttons (line 429) | fn collect_buttons(
type DeviceInfo (line 452) | struct DeviceInfo {
type EvCode (line 462) | pub struct EvCode {
method new (line 468) | fn new(page: u32, usage: u32) -> Self {
method into_u32 (line 472) | pub fn into_u32(self) -> u32 {
function from (line 478) | fn from(e: IOHIDElement) -> Self {
method fmt (line 487) | fn fmt(&self, f: &mut Formatter) -> FmtResult {
constant AXIS_LSTICKX (line 500) | pub const AXIS_LSTICKX: EvCode = EvCode {
constant AXIS_LSTICKY (line 504) | pub const AXIS_LSTICKY: EvCode = EvCode {
constant AXIS_LEFTZ (line 508) | pub const AXIS_LEFTZ: EvCode = EvCode {
constant AXIS_RSTICKX (line 512) | pub const AXIS_RSTICKX: EvCode = EvCode {
constant AXIS_RSTICKY (line 516) | pub const AXIS_RSTICKY: EvCode = EvCode {
constant AXIS_RIGHTZ (line 520) | pub const AXIS_RIGHTZ: EvCode = EvCode {
constant AXIS_DPADX (line 524) | pub const AXIS_DPADX: EvCode = EvCode {
constant AXIS_DPADY (line 528) | pub const AXIS_DPADY: EvCode = EvCode {
constant AXIS_RT (line 532) | pub const AXIS_RT: EvCode = EvCode {
constant AXIS_LT (line 536) | pub const AXIS_LT: EvCode = EvCode {
constant AXIS_RT2 (line 540) | pub const AXIS_RT2: EvCode = EvCode {
constant AXIS_LT2 (line 544) | pub const AXIS_LT2: EvCode = EvCode {
constant BTN_SOUTH (line 549) | pub const BTN_SOUTH: EvCode = EvCode {
constant BTN_EAST (line 553) | pub const BTN_EAST: EvCode = EvCode {
constant BTN_C (line 557) | pub const BTN_C: EvCode = EvCode {
constant BTN_NORTH (line 561) | pub const BTN_NORTH: EvCode = EvCode {
constant BTN_WEST (line 565) | pub const BTN_WEST: EvCode = EvCode {
constant BTN_Z (line 569) | pub const BTN_Z: EvCode = EvCode {
constant BTN_LT (line 573) | pub const BTN_LT: EvCode = EvCode {
constant BTN_RT (line 577) | pub const BTN_RT: EvCode = EvCode {
constant BTN_LT2 (line 581) | pub const BTN_LT2: EvCode = EvCode {
constant BTN_RT2 (line 585) | pub const BTN_RT2: EvCode = EvCode {
constant BTN_SELECT (line 589) | pub const BTN_SELECT: EvCode = EvCode {
constant BTN_START (line 593) | pub const BTN_START: EvCode = EvCode {
constant BTN_MODE (line 597) | pub const BTN_MODE: EvCode = EvCode {
constant BTN_LTHUMB (line 601) | pub const BTN_LTHUMB: EvCode = EvCode {
constant BTN_RTHUMB (line 605) | pub const BTN_RTHUMB: EvCode = EvCode {
constant BTN_DPAD_UP (line 610) | pub const BTN_DPAD_UP: EvCode = EvCode {
constant BTN_DPAD_DOWN (line 614) | pub const BTN_DPAD_DOWN: EvCode = EvCode {
constant BTN_DPAD_LEFT (line 618) | pub const BTN_DPAD_LEFT: EvCode = EvCode {
constant BTN_DPAD_RIGHT (line 622) | pub const BTN_DPAD_RIGHT: EvCode = EvCode {
type Context (line 628) | type Context = (Sender<(Event, Option<Device>)>, Arc<Mutex<Vec<DeviceInf...
function device_matching_cb (line 630) | extern "C-unwind" fn device_matching_cb(
function device_removal_cb (line 705) | unsafe extern "C-unwind" fn device_removal_cb(
function input_value_cb (line 740) | unsafe extern "C-unwind" fn input_value_cb(
FILE: gilrs-core/src/platform/macos/io_kit.rs
function new_manager (line 30) | pub fn new_manager() -> Option<CFRetained<IOHIDManager>> {
type Device (line 51) | pub struct Device(pub CFRetained<IOHIDDevice>);
type DeviceExt (line 57) | pub trait DeviceExt: Properties {
method device (line 58) | fn device(&self) -> &IOHIDDevice;
method get_name (line 60) | fn get_name(&self) -> Option<String> {
method get_location_id (line 65) | fn get_location_id(&self) -> Option<u32> {
method get_vendor_id (line 70) | fn get_vendor_id(&self) -> Option<u16> {
method get_product_id (line 75) | fn get_product_id(&self) -> Option<u16> {
method get_version (line 80) | fn get_version(&self) -> Option<u16> {
method get_page (line 85) | fn get_page(&self) -> Option<u32> {
method get_usage (line 90) | fn get_usage(&self) -> Option<u32> {
method get_service (line 95) | fn get_service(&self) -> Option<IOService> {
method device (line 116) | fn device(&self) -> &IOHIDDevice {
function device_elements (line 100) | pub fn device_elements(device: &IOHIDDevice) -> Vec<CFRetained<IOHIDElem...
function element_is_collection (line 136) | pub fn element_is_collection(type_: IOHIDElementType) -> bool {
function element_is_axis (line 140) | pub fn element_is_axis(type_: IOHIDElementType, page: u32, usage: u32) -...
function element_is_button (line 172) | pub fn element_is_button(type_: IOHIDElementType, page: u32, usage: u32)...
function element_is_hat (line 194) | pub fn element_is_hat(type_: IOHIDElementType, page: u32, usage: u32) ->...
function element_children (line 206) | pub fn element_children(element: &IOHIDElement) -> Vec<CFRetained<IOHIDE...
type IOService (line 236) | pub(crate) struct IOService(io_service_t);
method new (line 239) | pub fn new(io_service: io_service_t) -> Option<IOService> {
method get_registry_entry_id (line 254) | pub fn get_registry_entry_id(&self) -> Option<u64> {
method drop (line 272) | fn drop(&mut self) {
type Properties (line 277) | pub trait Properties {
method get_property (line 122) | fn get_property(&self, key: &CStr) -> Option<CFRetained<CFType>> {
method get_property (line 220) | fn get_property(&self, key: &CStr) -> Option<CFRetained<CFType>> {
method get_property (line 278) | fn get_property(&self, key: &CStr) -> Option<CFRetained<CFType>>;
method get_number_property (line 280) | fn get_number_property(&self, key: &CStr) -> Option<CFRetained<CFNumbe...
method get_string_property (line 285) | fn get_string_property(&self, key: &CStr) -> Option<CFRetained<CFStrin...
function create_hid_device_matcher (line 291) | fn create_hid_device_matcher(
constant PAGE_GENERIC_DESKTOP (line 308) | pub const PAGE_GENERIC_DESKTOP: u32 = kHIDPage_GenericDesktop;
constant PAGE_SIMULATION (line 309) | pub const PAGE_SIMULATION: u32 = kHIDPage_Simulation;
constant PAGE_BUTTON (line 310) | pub const PAGE_BUTTON: u32 = kHIDPage_Button;
constant USAGE_AXIS_LSTICKX (line 313) | pub const USAGE_AXIS_LSTICKX: u32 = kHIDUsage_GD_X;
constant USAGE_AXIS_LSTICKY (line 314) | pub const USAGE_AXIS_LSTICKY: u32 = kHIDUsage_GD_Y;
constant USAGE_AXIS_LEFTZ (line 315) | pub const USAGE_AXIS_LEFTZ: u32 = 0;
constant USAGE_AXIS_RSTICKX (line 316) | pub const USAGE_AXIS_RSTICKX: u32 = kHIDUsage_GD_Z;
constant USAGE_AXIS_RSTICKY (line 317) | pub const USAGE_AXIS_RSTICKY: u32 = kHIDUsage_GD_Rz;
constant USAGE_AXIS_RIGHTZ (line 318) | pub const USAGE_AXIS_RIGHTZ: u32 = 0;
constant USAGE_AXIS_DPADX (line 319) | pub const USAGE_AXIS_DPADX: u32 = kHIDUsage_GD_Hatswitch;
constant USAGE_AXIS_DPADY (line 320) | pub const USAGE_AXIS_DPADY: u32 = kHIDUsage_GD_Hatswitch + 1;
constant USAGE_AXIS_RT (line 321) | pub const USAGE_AXIS_RT: u32 = 0;
constant USAGE_AXIS_LT (line 322) | pub const USAGE_AXIS_LT: u32 = 0;
constant USAGE_AXIS_RT2 (line 323) | pub const USAGE_AXIS_RT2: u32 = kHIDUsage_Sim_Accelerator;
constant USAGE_AXIS_LT2 (line 324) | pub const USAGE_AXIS_LT2: u32 = kHIDUsage_Sim_Brake;
constant USAGE_BTN_SOUTH (line 327) | pub const USAGE_BTN_SOUTH: u32 = kHIDUsage_Button_1;
constant USAGE_BTN_EAST (line 328) | pub const USAGE_BTN_EAST: u32 = kHIDUsage_Button_1 + 1;
constant USAGE_BTN_WEST (line 329) | pub const USAGE_BTN_WEST: u32 = kHIDUsage_Button_1 + 3;
constant USAGE_BTN_NORTH (line 330) | pub const USAGE_BTN_NORTH: u32 = kHIDUsage_Button_1 + 4;
constant USAGE_BTN_LT (line 331) | pub const USAGE_BTN_LT: u32 = kHIDUsage_Button_1 + 6;
constant USAGE_BTN_RT (line 332) | pub const USAGE_BTN_RT: u32 = kHIDUsage_Button_1 + 7;
constant USAGE_BTN_LT2 (line 333) | pub const USAGE_BTN_LT2: u32 = kHIDUsage_Button_1 + 8;
constant USAGE_BTN_RT2 (line 334) | pub const USAGE_BTN_RT2: u32 = kHIDUsage_Button_1 + 9;
constant USAGE_BTN_SELECT (line 335) | pub const USAGE_BTN_SELECT: u32 = kHIDUsage_Button_1 + 10;
constant USAGE_BTN_START (line 336) | pub const USAGE_BTN_START: u32 = kHIDUsage_Button_1 + 11;
constant USAGE_BTN_MODE (line 337) | pub const USAGE_BTN_MODE: u32 = kHIDUsage_Button_1 + 12;
constant USAGE_BTN_LTHUMB (line 338) | pub const USAGE_BTN_LTHUMB: u32 = kHIDUsage_Button_1 + 13;
constant USAGE_BTN_RTHUMB (line 339) | pub const USAGE_BTN_RTHUMB: u32 = kHIDUsage_Button_1 + 14;
constant USAGE_BTN_DPAD_UP (line 340) | pub const USAGE_BTN_DPAD_UP: u32 = kHIDUsage_Button_1 + 15;
constant USAGE_BTN_DPAD_DOWN (line 341) | pub const USAGE_BTN_DPAD_DOWN: u32 = kHIDUsage_Button_1 + 16;
constant USAGE_BTN_DPAD_LEFT (line 342) | pub const USAGE_BTN_DPAD_LEFT: u32 = kHIDUsage_Button_1 + 17;
constant USAGE_BTN_DPAD_RIGHT (line 343) | pub const USAGE_BTN_DPAD_RIGHT: u32 = kHIDUsage_Button_1 + 18;
constant USAGE_BTN_C (line 344) | pub const USAGE_BTN_C: u32 = kHIDUsage_Button_1 + 19;
constant USAGE_BTN_Z (line 345) | pub const USAGE_BTN_Z: u32 = kHIDUsage_Button_1 + 20;
FILE: gilrs-core/src/platform/macos/mod.rs
constant IS_Y_AXIS_REVERSED (line 15) | pub const IS_Y_AXIS_REVERSED: bool = true;
FILE: gilrs-core/src/platform/wasm/ff.rs
type Device (line 11) | pub struct Device;
method set_ff_state (line 14) | pub fn set_ff_state(&mut self, _strong: u16, _weak: u16, _min_duration...
FILE: gilrs-core/src/platform/wasm/gamepad.rs
type Gilrs (line 24) | pub struct Gilrs {
method new (line 32) | pub(crate) fn new() -> Result<Self, PlatformError> {
method next_event (line 49) | pub(crate) fn next_event(&mut self) -> Option<Event> {
method next_event_blocking (line 182) | pub(crate) fn next_event_blocking(&mut self, _timeout: Option<Duration...
method gamepad (line 186) | pub fn gamepad(&self, id: usize) -> Option<&Gamepad> {
method last_gamepad_hint (line 190) | pub fn last_gamepad_hint(&self) -> usize {
type Mapping (line 197) | enum Mapping {
method buttons (line 209) | fn buttons(&self) -> &[(bool, f64)] {
method buttons_mut (line 216) | fn buttons_mut(&mut self) -> &mut [(bool, f64)] {
method axes (line 223) | fn axes(&self) -> &[f64] {
method axes_mut (line 230) | fn axes_mut(&mut self) -> &mut [f64] {
type Gamepad (line 239) | pub struct Gamepad {
method new (line 250) | fn new(gamepad: WebGamepad) -> Gamepad {
method name (line 327) | pub fn name(&self) -> &str {
method uuid (line 331) | pub fn uuid(&self) -> Uuid {
method vendor_id (line 335) | pub fn vendor_id(&self) -> Option<u16> {
method product_id (line 339) | pub fn product_id(&self) -> Option<u16> {
method is_connected (line 343) | pub fn is_connected(&self) -> bool {
method power_info (line 347) | pub fn power_info(&self) -> PowerInfo {
method is_ff_supported (line 351) | pub fn is_ff_supported(&self) -> bool {
method ff_device (line 355) | pub fn ff_device(&self) -> Option<FfDevice> {
method buttons (line 359) | pub fn buttons(&self) -> &[EvCode] {
method axes (line 363) | pub fn axes(&self) -> &[EvCode] {
method button_code (line 367) | fn button_code(&self, index: usize) -> EvCode {
method axis_code (line 374) | fn axis_code(&self, index: usize) -> EvCode {
method axis_info (line 381) | pub(crate) fn axis_info(&self, _nec: EvCode) -> Option<&AxisInfo> {
type EvCode (line 399) | pub struct EvCode(u8);
method into_u32 (line 402) | pub fn into_u32(self) -> u32 {
method fmt (line 408) | fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
type Error (line 414) | enum Error {
method fmt (line 419) | fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
constant AXIS_LSTICKX (line 431) | pub const AXIS_LSTICKX: EvCode = EvCode(0);
constant AXIS_LSTICKY (line 432) | pub const AXIS_LSTICKY: EvCode = EvCode(1);
constant AXIS_LEFTZ (line 433) | pub const AXIS_LEFTZ: EvCode = EvCode(2);
constant AXIS_RSTICKX (line 434) | pub const AXIS_RSTICKX: EvCode = EvCode(3);
constant AXIS_RSTICKY (line 435) | pub const AXIS_RSTICKY: EvCode = EvCode(4);
constant AXIS_RIGHTZ (line 436) | pub const AXIS_RIGHTZ: EvCode = EvCode(5);
constant AXIS_DPADX (line 437) | pub const AXIS_DPADX: EvCode = EvCode(6);
constant AXIS_DPADY (line 438) | pub const AXIS_DPADY: EvCode = EvCode(7);
constant AXIS_RT (line 439) | pub const AXIS_RT: EvCode = EvCode(8);
constant AXIS_LT (line 440) | pub const AXIS_LT: EvCode = EvCode(9);
constant AXIS_RT2 (line 441) | pub const AXIS_RT2: EvCode = EvCode(10);
constant AXIS_LT2 (line 442) | pub const AXIS_LT2: EvCode = EvCode(11);
constant BTN_SOUTH (line 444) | pub const BTN_SOUTH: EvCode = EvCode(12);
constant BTN_EAST (line 445) | pub const BTN_EAST: EvCode = EvCode(13);
constant BTN_C (line 446) | pub const BTN_C: EvCode = EvCode(14);
constant BTN_NORTH (line 447) | pub const BTN_NORTH: EvCode = EvCode(15);
constant BTN_WEST (line 448) | pub const BTN_WEST: EvCode = EvCode(16);
constant BTN_Z (line 449) | pub const BTN_Z: EvCode = EvCode(17);
constant BTN_LT (line 450) | pub const BTN_LT: EvCode = EvCode(18);
constant BTN_RT (line 451) | pub const BTN_RT: EvCode = EvCode(19);
constant BTN_LT2 (line 452) | pub const BTN_LT2: EvCode = EvCode(20);
constant BTN_RT2 (line 453) | pub const BTN_RT2: EvCode = EvCode(21);
constant BTN_SELECT (line 454) | pub const BTN_SELECT: EvCode = EvCode(22);
constant BTN_START (line 455) | pub const BTN_START: EvCode = EvCode(23);
constant BTN_MODE (line 456) | pub const BTN_MODE: EvCode = EvCode(24);
constant BTN_LTHUMB (line 457) | pub const BTN_LTHUMB: EvCode = EvCode(25);
constant BTN_RTHUMB (line 458) | pub const BTN_RTHUMB: EvCode = EvCode(26);
constant BTN_DPAD_UP (line 460) | pub const BTN_DPAD_UP: EvCode = EvCode(27);
constant BTN_DPAD_DOWN (line 461) | pub const BTN_DPAD_DOWN: EvCode = EvCode(28);
constant BTN_DPAD_LEFT (line 462) | pub const BTN_DPAD_LEFT: EvCode = EvCode(29);
constant BTN_DPAD_RIGHT (line 463) | pub const BTN_DPAD_RIGHT: EvCode = EvCode(30);
FILE: gilrs-core/src/platform/wasm/mod.rs
constant IS_Y_AXIS_REVERSED (line 7) | pub const IS_Y_AXIS_REVERSED: bool = true;
FILE: gilrs-core/src/platform/windows_wgi/ff.rs
type Device (line 12) | pub struct Device {
method new (line 18) | pub(crate) fn new(id: u32, wgi_gamepad: Option<WgiGamepad>) -> Self {
method set_ff_state (line 22) | pub fn set_ff_state(&mut self, strong: u16, weak: u16, _min_duration: ...
FILE: gilrs-core/src/platform/windows_wgi/gamepad.rs
constant SDL_HARDWARE_BUS_USB (line 29) | const SDL_HARDWARE_BUS_USB: u32 = 0x03;
constant EVENT_THREAD_SLEEP_TIME (line 35) | const EVENT_THREAD_SLEEP_TIME: u64 = 8;
constant WGI_TO_GILRS_BUTTON_MAP (line 37) | const WGI_TO_GILRS_BUTTON_MAP: [(GamepadButtons, crate::EvCode); 14] = [
type WgiEvent (line 57) | struct WgiEvent {
method new (line 64) | fn new(raw_game_controller: RawGameController, event: EventType) -> Se...
type Gilrs (line 75) | pub struct Gilrs {
method new (line 83) | pub(crate) fn new() -> Result<Self, PlatformError> {
method spawn_thread (line 112) | fn spawn_thread(tx: Sender<WgiEvent>, stop_rx: Receiver<()>) -> JoinHa...
method next_event (line 239) | pub(crate) fn next_event(&mut self) -> Option<Event> {
method next_event_blocking (line 246) | pub(crate) fn next_event_blocking(&mut self, timeout: Option<Duration>...
method handle_event (line 260) | fn handle_event(&mut self, wgi_event: WgiEvent) -> Event {
method gamepad (line 291) | pub fn gamepad(&self, id: usize) -> Option<&Gamepad> {
method last_gamepad_hint (line 295) | pub fn last_gamepad_hint(&self) -> usize {
method drop (line 301) | fn drop(&mut self) {
type RawGamepadReading (line 312) | struct RawGamepadReading {
method new (line 320) | fn new(raw_game_controller: &RawGameController) -> windows::core::Resu...
method update (line 338) | fn update(&mut self, raw_game_controller: &RawGameController) -> windo...
function direction_from_switch (line 351) | fn direction_from_switch(switch: GameControllerSwitchPosition) -> (i32, ...
type Reading (line 366) | enum Reading {
method time (line 372) | fn time(&self) -> u64 {
method update (line 379) | fn update(&mut self, controller: &RawGameController) -> windows::core:...
method send_events_for_differences (line 392) | fn send_events_for_differences(
method is_gamepad (line 514) | fn is_gamepad(&self) -> bool {
type Gamepad (line 520) | pub struct Gamepad {
method new (line 542) | fn new(id: u32, raw_game_controller: RawGameController) -> Gamepad {
method name (line 605) | pub fn name(&self) -> &str {
method uuid (line 609) | pub fn uuid(&self) -> Uuid {
method vendor_id (line 613) | pub fn vendor_id(&self) -> Option<u16> {
method product_id (line 617) | pub fn product_id(&self) -> Option<u16> {
method is_connected (line 621) | pub fn is_connected(&self) -> bool {
method power_info (line 625) | pub fn power_info(&self) -> PowerInfo {
method power_info_err (line 630) | fn power_info_err(&self) -> windows::core::Result<PowerInfo> {
method is_ff_supported (line 656) | pub fn is_ff_supported(&self) -> bool {
method ff_device (line 666) | pub fn ff_device(&self) -> Option<FfDevice> {
method buttons (line 670) | pub fn buttons(&self) -> &[EvCode] {
method axes (line 677) | pub fn axes(&self) -> &[EvCode] {
method axis_info (line 684) | pub(crate) fn axis_info(&self, nec: EvCode) -> Option<&AxisInfo> {
method collect_axes_and_buttons (line 720) | fn collect_axes_and_buttons(&mut self) {
type EvCodeKind (line 760) | enum EvCodeKind {
method fmt (line 767) | fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
type EvCode (line 779) | pub struct EvCode {
method into_u32 (line 785) | pub fn into_u32(self) -> u32 {
method fmt (line 791) | fn fmt(&self, f: &mut Formatter) -> FmtResult {
constant AXIS_LSTICKX (line 799) | pub const AXIS_LSTICKX: EvCode = EvCode {
constant AXIS_LSTICKY (line 803) | pub const AXIS_LSTICKY: EvCode = EvCode {
constant AXIS_RSTICKX (line 807) | pub const AXIS_RSTICKX: EvCode = EvCode {
constant AXIS_LT2 (line 811) | pub const AXIS_LT2: EvCode = EvCode {
constant AXIS_RT2 (line 815) | pub const AXIS_RT2: EvCode = EvCode {
constant AXIS_RSTICKY (line 819) | pub const AXIS_RSTICKY: EvCode = EvCode {
constant AXIS_RT (line 823) | pub const AXIS_RT: EvCode = EvCode {
constant AXIS_LT (line 827) | pub const AXIS_LT: EvCode = EvCode {
constant AXIS_LEFTZ (line 831) | pub const AXIS_LEFTZ: EvCode = EvCode {
constant AXIS_RIGHTZ (line 835) | pub const AXIS_RIGHTZ: EvCode = EvCode {
constant AXIS_DPADX (line 840) | pub const AXIS_DPADX: EvCode = EvCode {
constant AXIS_DPADY (line 844) | pub const AXIS_DPADY: EvCode = EvCode {
constant BTN_WEST (line 849) | pub const BTN_WEST: EvCode = EvCode {
constant BTN_SOUTH (line 853) | pub const BTN_SOUTH: EvCode = EvCode {
constant BTN_EAST (line 857) | pub const BTN_EAST: EvCode = EvCode {
constant BTN_NORTH (line 861) | pub const BTN_NORTH: EvCode = EvCode {
constant BTN_LT (line 865) | pub const BTN_LT: EvCode = EvCode {
constant BTN_RT (line 869) | pub const BTN_RT: EvCode = EvCode {
constant BTN_LT2 (line 873) | pub const BTN_LT2: EvCode = EvCode {
constant BTN_RT2 (line 877) | pub const BTN_RT2: EvCode = EvCode {
constant BTN_SELECT (line 881) | pub const BTN_SELECT: EvCode = EvCode {
constant BTN_START (line 885) | pub const BTN_START: EvCode = EvCode {
constant BTN_LTHUMB (line 889) | pub const BTN_LTHUMB: EvCode = EvCode {
constant BTN_RTHUMB (line 893) | pub const BTN_RTHUMB: EvCode = EvCode {
constant BTN_MODE (line 897) | pub const BTN_MODE: EvCode = EvCode {
constant BTN_C (line 901) | pub const BTN_C: EvCode = EvCode {
constant BTN_Z (line 905) | pub const BTN_Z: EvCode = EvCode {
constant BTN_DPAD_UP (line 915) | pub const BTN_DPAD_UP: EvCode = EvCode {
constant BTN_DPAD_RIGHT (line 919) | pub const BTN_DPAD_RIGHT: EvCode = EvCode {
constant BTN_DPAD_DOWN (line 923) | pub const BTN_DPAD_DOWN: EvCode = EvCode {
constant BTN_DPAD_LEFT (line 927) | pub const BTN_DPAD_LEFT: EvCode = EvCode {
FILE: gilrs-core/src/platform/windows_wgi/mod.rs
constant IS_Y_AXIS_REVERSED (line 13) | pub const IS_Y_AXIS_REVERSED: bool = true;
FILE: gilrs-core/src/platform/windows_xinput/ff.rs
type Device (line 13) | pub struct Device {
method new (line 19) | pub(crate) fn new(id: u32, xinput_handle: Arc<XInputHandle>) -> Self {
method set_ff_state (line 23) | pub fn set_ff_state(&mut self, strong: u16, weak: u16, _min_duration: ...
FILE: gilrs-core/src/platform/windows_xinput/gamepad.rs
constant EVENT_THREAD_SLEEP_TIME (line 33) | const EVENT_THREAD_SLEEP_TIME: u64 = 10;
constant ITERATIONS_TO_CHECK_IF_CONNECTED (line 34) | const ITERATIONS_TO_CHECK_IF_CONNECTED: u64 = 100;
constant MAX_XINPUT_CONTROLLERS (line 36) | const MAX_XINPUT_CONTROLLERS: usize = 4;
type Gilrs (line 39) | pub struct Gilrs {
method new (line 45) | pub(crate) fn new() -> Result<Self, PlatformError> {
method next_event (line 69) | pub(crate) fn next_event(&mut self) -> Option<Event> {
method next_event_blocking (line 76) | pub(crate) fn next_event_blocking(&mut self, timeout: Option<Duration>...
method handle_evevnt (line 88) | fn handle_evevnt(&mut self, ev: Option<Event>) {
method gamepad (line 98) | pub fn gamepad(&self, id: usize) -> Option<&Gamepad> {
method last_gamepad_hint (line 102) | pub fn last_gamepad_hint(&self) -> usize {
method spawn_thread (line 106) | fn spawn_thread(
method compare_state (line 159) | fn compare_state(id: usize, g: &XGamepad, pg: &XGamepad, tx: &Sender<E...
type Gamepad (line 386) | pub struct Gamepad {
method new (line 394) | fn new(id: u32, xinput_handle: Arc<XInputHandle>) -> Gamepad {
method name (line 405) | pub fn name(&self) -> &str {
method uuid (line 409) | pub fn uuid(&self) -> Uuid {
method vendor_id (line 413) | pub fn vendor_id(&self) -> Option<u16> {
method product_id (line 417) | pub fn product_id(&self) -> Option<u16> {
method is_connected (line 421) | pub fn is_connected(&self) -> bool {
method power_info (line 425) | pub fn power_info(&self) -> PowerInfo {
method is_ff_supported (line 457) | pub fn is_ff_supported(&self) -> bool {
method ff_device (line 461) | pub fn ff_device(&self) -> Option<FfDevice> {
method buttons (line 465) | pub fn buttons(&self) -> &[EvCode] {
method axes (line 469) | pub fn axes(&self) -> &[EvCode] {
method axis_info (line 473) | pub(crate) fn axis_info(&self, nec: EvCode) -> Option<&AxisInfo> {
function is_mask_eq (line 481) | fn is_mask_eq(l: u16, r: u16, mask: u16) -> bool {
type EvCode (line 490) | pub struct EvCode(u8);
method into_u32 (line 493) | pub fn into_u32(self) -> u32 {
method fmt (line 499) | fn fmt(&self, f: &mut Formatter) -> FmtResult {
type Error (line 505) | enum Error {
method fmt (line 512) | fn fmt(&self, f: &mut Formatter) -> FmtResult {
constant AXIS_LSTICKX (line 530) | pub const AXIS_LSTICKX: EvCode = EvCode(0);
constant AXIS_LSTICKY (line 531) | pub const AXIS_LSTICKY: EvCode = EvCode(1);
constant AXIS_LEFTZ (line 532) | pub const AXIS_LEFTZ: EvCode = EvCode(2);
constant AXIS_RSTICKX (line 533) | pub const AXIS_RSTICKX: EvCode = EvCode(3);
constant AXIS_RSTICKY (line 534) | pub const AXIS_RSTICKY: EvCode = EvCode(4);
constant AXIS_RIGHTZ (line 535) | pub const AXIS_RIGHTZ: EvCode = EvCode(5);
constant AXIS_DPADX (line 536) | pub const AXIS_DPADX: EvCode = EvCode(6);
constant AXIS_DPADY (line 537) | pub const AXIS_DPADY: EvCode = EvCode(7);
constant AXIS_RT (line 538) | pub const AXIS_RT: EvCode = EvCode(8);
constant AXIS_LT (line 539) | pub const AXIS_LT: EvCode = EvCode(9);
constant AXIS_RT2 (line 540) | pub const AXIS_RT2: EvCode = EvCode(10);
constant AXIS_LT2 (line 541) | pub const AXIS_LT2: EvCode = EvCode(11);
constant BTN_SOUTH (line 543) | pub const BTN_SOUTH: EvCode = EvCode(12);
constant BTN_EAST (line 544) | pub const BTN_EAST: EvCode = EvCode(13);
constant BTN_C (line 545) | pub const BTN_C: EvCode = EvCode(14);
constant BTN_NORTH (line 546) | pub const BTN_NORTH: EvCode = EvCode(15);
constant BTN_WEST (line 547) | pub const BTN_WEST: EvCode = EvCode(16);
constant BTN_Z (line 548) | pub const BTN_Z: EvCode = EvCode(17);
constant BTN_LT (line 549) | pub const BTN_LT: EvCode = EvCode(18);
constant BTN_RT (line 550) | pub const BTN_RT: EvCode = EvCode(19);
constant BTN_LT2 (line 551) | pub const BTN_LT2: EvCode = EvCode(20);
constant BTN_RT2 (line 552) | pub const BTN_RT2: EvCode = EvCode(21);
constant BTN_SELECT (line 553) | pub const BTN_SELECT: EvCode = EvCode(22);
constant BTN_START (line 554) | pub const BTN_START: EvCode = EvCode(23);
constant BTN_MODE (line 555) | pub const BTN_MODE: EvCode = EvCode(24);
constant BTN_LTHUMB (line 556) | pub const BTN_LTHUMB: EvCode = EvCode(25);
constant BTN_RTHUMB (line 557) | pub const BTN_RTHUMB: EvCode = EvCode(26);
constant BTN_DPAD_UP (line 559) | pub const BTN_DPAD_UP: EvCode = EvCode(27);
constant BTN_DPAD_DOWN (line 560) | pub const BTN_DPAD_DOWN: EvCode = EvCode(28);
constant BTN_DPAD_LEFT (line 561) | pub const BTN_DPAD_LEFT: EvCode = EvCode(29);
constant BTN_DPAD_RIGHT (line 562) | pub const BTN_DPAD_RIGHT: EvCode = EvCode(30);
FILE: gilrs-core/src/platform/windows_xinput/mod.rs
constant IS_Y_AXIS_REVERSED (line 13) | pub const IS_Y_AXIS_REVERSED: bool = false;
FILE: gilrs-core/src/utils.rs
function test_bit (line 5) | pub(crate) fn test_bit(n: u16, array: &[u8]) -> bool {
function time_now (line 10) | pub fn time_now() -> SystemTime {
function time_now (line 15) | pub fn time_now() -> SystemTime {
FILE: gilrs/build.rs
constant PATH_SEPARATOR (line 14) | const PATH_SEPARATOR: &str = "backslash";
constant PATH_SEPARATOR (line 17) | const PATH_SEPARATOR: &str = "slash";
function main (line 19) | fn main() {
FILE: gilrs/examples/ev.rs
function main (line 13) | fn main() {
FILE: gilrs/examples/ff.rs
function main (line 13) | fn main() {
FILE: gilrs/examples/ff_pos.rs
type Modify (line 16) | enum Modify {
method next (line 24) | fn next(&mut self) {
method prev (line 36) | fn prev(&mut self) {
function main (line 49) | fn main() {
FILE: gilrs/examples/gamepad_info.rs
function main (line 4) | fn main() {
FILE: gilrs/examples/gui.rs
type MyEguiApp (line 14) | struct MyEguiApp {
method log (line 59) | fn log(&mut self, message: String) {
method new (line 66) | fn new(_cc: &eframe::CreationContext<'_>) -> Self {
method update (line 72) | fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
method default (line 25) | fn default() -> Self {
function main (line 308) | fn main() {
function main (line 322) | fn main() {
FILE: gilrs/src/constants.rs
constant BTN_UNKNOWN (line 8) | pub const BTN_UNKNOWN: u16 = 0;
constant BTN_SOUTH (line 10) | pub const BTN_SOUTH: u16 = 1;
constant BTN_EAST (line 11) | pub const BTN_EAST: u16 = 2;
constant BTN_C (line 12) | pub const BTN_C: u16 = 3;
constant BTN_NORTH (line 13) | pub const BTN_NORTH: u16 = 4;
constant BTN_WEST (line 14) | pub const BTN_WEST: u16 = 5;
constant BTN_Z (line 15) | pub const BTN_Z: u16 = 6;
constant BTN_LT (line 16) | pub const BTN_LT: u16 = 7;
constant BTN_RT (line 17) | pub const BTN_RT: u16 = 8;
constant BTN_LT2 (line 18) | pub const BTN_LT2: u16 = 9;
constant BTN_RT2 (line 19) | pub const BTN_RT2: u16 = 10;
constant BTN_SELECT (line 20) | pub const BTN_SELECT: u16 = 11;
constant BTN_START (line 21) | pub const BTN_START: u16 = 12;
constant BTN_MODE (line 22) | pub const BTN_MODE: u16 = 13;
constant BTN_LTHUMB (line 23) | pub const BTN_LTHUMB: u16 = 14;
constant BTN_RTHUMB (line 24) | pub const BTN_RTHUMB: u16 = 15;
constant BTN_DPAD_UP (line 26) | pub const BTN_DPAD_UP: u16 = 16;
constant BTN_DPAD_DOWN (line 27) | pub const BTN_DPAD_DOWN: u16 = 17;
constant BTN_DPAD_LEFT (line 28) | pub const BTN_DPAD_LEFT: u16 = 18;
constant BTN_DPAD_RIGHT (line 29) | pub const BTN_DPAD_RIGHT: u16 = 19;
constant AXIS_UNKNOWN (line 31) | pub const AXIS_UNKNOWN: u16 = 0;
constant AXIS_LSTICKX (line 33) | pub const AXIS_LSTICKX: u16 = 1;
constant AXIS_LSTICKY (line 34) | pub const AXIS_LSTICKY: u16 = 2;
constant AXIS_LEFTZ (line 35) | pub const AXIS_LEFTZ: u16 = 3;
constant AXIS_RSTICKX (line 36) | pub const AXIS_RSTICKX: u16 = 4;
constant AXIS_RSTICKY (line 37) | pub const AXIS_RSTICKY: u16 = 5;
constant AXIS_RIGHTZ (line 38) | pub const AXIS_RIGHTZ: u16 = 6;
constant AXIS_DPADX (line 39) | pub const AXIS_DPADX: u16 = 7;
constant AXIS_DPADY (line 40) | pub const AXIS_DPADY: u16 = 8;
FILE: gilrs/src/ev/filter.rs
type Jitter (line 84) | pub struct Jitter {
method new (line 90) | pub fn new() -> Self {
method default (line 96) | fn default() -> Self {
function apply_deadzone (line 119) | fn apply_deadzone(x: f32, y: f32, threshold: f32) -> (f32, f32) {
function deadzone_nonzero_axis_idx (line 129) | fn deadzone_nonzero_axis_idx(axis: Axis) -> Option<usize> {
function deadzone (line 144) | pub fn deadzone(ev: Option<Event>, gilrs: &mut Gilrs) -> Option<Event> {
function axis_dpad_to_button (line 241) | pub fn axis_dpad_to_button(ev: Option<Event>, gilrs: &mut Gilrs) -> Opti...
type Repeat (line 434) | pub struct Repeat {
method new (line 441) | pub fn new() -> Self {
method default (line 450) | fn default() -> Self {
type Filter (line 505) | pub trait Filter {
method filter_ev (line 506) | fn filter_ev<F: FilterFn>(&self, filter: &F, gilrs: &mut Gilrs) -> Opt...
method filter_ev (line 526) | fn filter_ev<F: FilterFn>(&self, filter: &F, gilrs: &mut Gilrs) -> Opt...
method filter_ev (line 538) | fn filter_ev<F: FilterFn>(&self, filter: &F, gilrs: &mut Gilrs) -> Opt...
type FilterFn (line 512) | pub trait FilterFn {
method filter (line 102) | fn filter(&self, ev: Option<Event>, gilrs: &mut Gilrs) -> Option<Event> {
method filter (line 456) | fn filter(&self, ev: Option<Event>, gilrs: &mut Gilrs) -> Option<Event> {
method filter (line 513) | fn filter(&self, ev: Option<Event>, gilrs: &mut Gilrs) -> Option<Event>;
method filter (line 520) | fn filter(&self, ev: Option<Event>, gilrs: &mut Gilrs) -> Option<Event> {
FILE: gilrs/src/ev/mod.rs
type Code (line 34) | pub struct Code(pub(crate) gilrs_core::EvCode);
method into_u32 (line 43) | pub fn into_u32(&self) -> u32 {
method fmt (line 37) | fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
type Event (line 52) | pub struct Event {
method new (line 63) | pub fn new(id: GamepadId, event: EventType) -> Self {
method drop (line 72) | pub fn drop(mut self) -> Event {
method is_dropped (line 79) | pub fn is_dropped(&self) -> bool {
type EventType (line 88) | pub enum EventType {
type Button (line 116) | pub enum Button {
method is_action (line 147) | pub fn is_action(self) -> bool {
method is_trigger (line 152) | pub fn is_trigger(self) -> bool {
method is_menu (line 160) | pub fn is_menu(self) -> bool {
method is_stick (line 165) | pub fn is_stick(self) -> bool {
method is_dpad (line 170) | pub fn is_dpad(self) -> bool {
method to_nec (line 175) | pub fn to_nec(self) -> Option<Code> {
type Axis (line 210) | pub enum Axis {
method is_stick (line 224) | pub fn is_stick(self) -> bool {
method second_axis (line 240) | pub fn second_axis(self) -> Option<Self> {
type AxisOrBtn (line 257) | pub enum AxisOrBtn {
method is_button (line 263) | pub(crate) fn is_button(&self) -> bool {
FILE: gilrs/src/ev/state.rs
type GamepadState (line 18) | pub struct GamepadState {
method new (line 26) | pub(crate) fn new() -> Self {
method is_pressed (line 35) | pub fn is_pressed(&self, btn: Code) -> bool {
method value (line 44) | pub fn value(&self, el: Code) -> f32 {
method buttons (line 53) | pub fn buttons(&self) -> ButtonDataIter<'_> {
method axes (line 58) | pub fn axes(&self) -> AxisDataIter<'_> {
method button_data (line 63) | pub fn button_data(&self, btn: Code) -> Option<&ButtonData> {
method axis_data (line 68) | pub fn axis_data(&self, axis: Code) -> Option<&AxisData> {
method set_btn_pressed (line 72) | pub(crate) fn set_btn_pressed(
method set_btn_repeating (line 94) | pub(crate) fn set_btn_repeating(&mut self, btn: Code, counter: u64, ti...
method set_btn_value (line 104) | pub(crate) fn set_btn_value(
method update_axis (line 120) | pub(crate) fn update_axis(&mut self, axis: Code, data: AxisData) {
type ButtonDataIter (line 126) | pub struct ButtonDataIter<'a>(hash_map::Iter<'a, Code, ButtonData>);
type AxisDataIter (line 129) | pub struct AxisDataIter<'a>(hash_map::Iter<'a, Code, AxisData>);
type Item (line 132) | type Item = (Code, &'a ButtonData);
method next (line 134) | fn next(&mut self) -> Option<Self::Item> {
type Item (line 140) | type Item = (Code, &'a AxisData);
method next (line 142) | fn next(&mut self) -> Option<Self::Item> {
type ButtonData (line 149) | pub struct ButtonData {
method new (line 158) | pub(crate) fn new(
method is_pressed (line 175) | pub fn is_pressed(&self) -> bool {
method value (line 180) | pub fn value(&self) -> f32 {
method is_repeating (line 185) | pub fn is_repeating(&self) -> bool {
method counter (line 190) | pub fn counter(&self) -> u64 {
method timestamp (line 195) | pub fn timestamp(&self) -> SystemTime {
type AxisData (line 202) | pub struct AxisData {
method new (line 209) | pub(crate) fn new(value: f32, counter: u64, time: SystemTime) -> Self {
method value (line 217) | pub fn value(&self) -> f32 {
method counter (line 222) | pub fn counter(&self) -> u64 {
method timestamp (line 227) | pub fn timestamp(&self) -> SystemTime {
FILE: gilrs/src/ff/base_effect.rs
type BaseEffectType (line 18) | pub enum BaseEffectType {
method magnitude (line 24) | fn magnitude(&self) -> u16 {
type Output (line 33) | type Output = BaseEffectType;
method mul (line 35) | fn mul(self, rhs: f32) -> Self::Output {
method default (line 45) | fn default() -> Self {
type BaseEffect (line 55) | pub struct BaseEffect {
method magnitude_at (line 67) | pub(super) fn magnitude_at(&self, ticks: Ticks) -> BaseEffectType {
type Envelope (line 81) | pub struct Envelope {
method at (line 89) | pub(super) fn at(&self, ticks: Ticks, dur: Ticks) -> f32 {
type Replay (line 114) | pub struct Replay {
method at (line 124) | pub(super) fn at(&self, ticks: Ticks) -> f32 {
method dur (line 133) | pub fn dur(&self) -> Ticks {
method wrap (line 138) | fn wrap(&self, ticks: Ticks) -> Option<Ticks> {
method default (line 144) | fn default() -> Self {
FILE: gilrs/src/ff/effect_source.rs
type DistanceModel (line 28) | pub enum DistanceModel {
method attenuation (line 69) | fn attenuation(self, mut distance: f32) -> f32 {
method validate (line 126) | pub(crate) fn validate(self) -> Result<(), DistanceModelError> {
type DistanceModelError (line 211) | pub enum DistanceModelError {
method fmt (line 225) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
type EffectState (line 238) | pub(super) enum EffectState {
type EffectSource (line 244) | pub(crate) struct EffectSource {
method new (line 257) | pub(super) fn new(
method combine_base_effects (line 277) | pub(super) fn combine_base_effects(&mut self, ticks: Ticks, actor_pos:...
method flush_completion_events (line 319) | pub(super) fn flush_completion_events(&mut self) -> Vec<Event> {
type Magnitude (line 326) | pub(super) struct Magnitude {
method zero (line 332) | pub fn zero() -> Self {
type Output (line 338) | type Output = Magnitude;
method mul (line 340) | fn mul(self, rhs: f32) -> Self::Output {
method add_assign (line 359) | fn add_assign(&mut self, rhs: Magnitude) {
type SliceVecExt (line 365) | trait SliceVecExt {
method distance (line 368) | fn distance(self, from: Self) -> Self::Base;
type Base (line 372) | type Base = f32;
method distance (line 374) | fn distance(self, from: Self) -> f32 {
FILE: gilrs/src/ff/mod.rs
type Effect (line 85) | pub struct Effect {
method play (line 122) | pub fn play(&self) -> Result<(), Error> {
method stop (line 128) | pub fn stop(&self) -> Result<(), Error> {
method set_gamepads (line 141) | pub fn set_gamepads(&self, ids: &[GamepadId], gilrs: &Gilrs) -> Result...
method add_gamepad (line 170) | pub fn add_gamepad(&self, gamepad: &Gamepad<'_>) -> Result<(), Error> {
method set_repeat (line 186) | pub fn set_repeat(&self, repeat: Repeat) -> Result<(), Error> {
method set_distance_model (line 201) | pub fn set_distance_model(&self, model: DistanceModel) -> Result<(), E...
method set_position (line 210) | pub fn set_position<Vec3f: Into<[f32; 3]>>(&self, position: Vec3f) -> ...
method set_gain (line 221) | pub fn set_gain(&self, gain: f32) -> Result<(), Error> {
method eq (line 91) | fn eq(&self, other: &Effect) -> bool {
method hash (line 99) | fn hash<H: Hasher>(&self, state: &mut H) {
method clone (line 105) | fn clone(&self) -> Self {
method drop (line 115) | fn drop(&mut self) {
type EffectBuilder (line 231) | pub struct EffectBuilder {
method new (line 244) | pub fn new() -> Self {
method add_effect (line 256) | pub fn add_effect(&mut self, effect: BaseEffect) -> &mut Self {
method gamepads (line 263) | pub fn gamepads(&mut self, ids: &[GamepadId]) -> &mut Self {
method add_gamepad (line 271) | pub fn add_gamepad(&mut self, gamepad: &Gamepad<'_>) -> &mut Self {
method repeat (line 278) | pub fn repeat(&mut self, repeat: Repeat) -> &mut Self {
method distance_model (line 284) | pub fn distance_model(&mut self, model: DistanceModel) -> &mut Self {
method position (line 290) | pub fn position<Vec3f: Into<[f32; 3]>>(&mut self, position: Vec3f) -> ...
method gain (line 296) | pub fn gain(&mut self, gain: f32) -> &mut Self {
method finish (line 310) | pub fn finish(&mut self, gilrs: &mut Gilrs) -> Result<Effect, Error> {
method default (line 343) | fn default() -> Self {
type Error (line 351) | pub enum Error {
method fmt (line 374) | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
method from (line 398) | fn from(_: SendError<T>) -> Self {
method from (line 404) | fn from(f: DistanceModelError) -> Self {
method source (line 365) | fn source(&self) -> Option<&(dyn StdError + 'static)> {
function envelope (line 414) | fn envelope() {
function envelope_default (line 433) | fn envelope_default() {
function replay (line 443) | fn replay() {
FILE: gilrs/src/ff/server.rs
type Message (line 23) | pub(crate) enum Message {
method use_trace_level (line 83) | fn use_trace_level(&self) -> bool {
type FfMessage (line 77) | pub(crate) enum FfMessage {
type Device (line 94) | struct Device {
method from (line 138) | fn from(inner: FfDevice) -> Self {
type Effect (line 99) | struct Effect {
method inc (line 106) | fn inc(&mut self) -> usize {
method dec (line 111) | fn dec(&mut self) -> usize {
method from (line 118) | fn from(source: EffectSource) -> Self {
type Target (line 124) | type Target = EffectSource;
method deref (line 126) | fn deref(&self) -> &Self::Target {
method deref_mut (line 132) | fn deref_mut(&mut self) -> &mut Self::Target {
function run (line 146) | pub(crate) fn run(tx: Sender<FfMessage>, rx: Receiver<Message>) {
function init (line 279) | pub(crate) fn init() -> (Sender<Message>, Receiver<FfMessage>) {
function combine_and_play (line 293) | fn combine_and_play(
FILE: gilrs/src/ff/time.rs
constant TICK_DURATION (line 13) | pub(crate) const TICK_DURATION: u32 = 50;
type Ticks (line 34) | pub struct Ticks(pub(super) u32);
method from_ms (line 37) | pub fn from_ms(dur: u32) -> Self {
method inc (line 41) | pub(super) fn inc(&mut self) {
method checked_sub (line 45) | pub(super) fn checked_sub(self, rhs: Ticks) -> Option<Ticks> {
method from (line 51) | fn from(dur: Duration) -> Self {
type Output (line 85) | type Output = Ticks;
method mul (line 87) | fn mul(self, rhs: u32) -> Self::Output {
method mul_assign (line 93) | fn mul_assign(&mut self, rhs: u32) {
type Output (line 57) | type Output = Ticks;
method add (line 59) | fn add(self, rhs: Ticks) -> Self::Output {
method add_assign (line 65) | fn add_assign(&mut self, rhs: Ticks) {
type Output (line 71) | type Output = Ticks;
method sub (line 73) | fn sub(self, rhs: Ticks) -> Self::Output {
method sub_assign (line 79) | fn sub_assign(&mut self, rhs: Ticks) {
type Output (line 99) | type Output = Ticks;
method rem (line 101) | fn rem(self, rhs: Ticks) -> Self::Output {
type Repeat (line 108) | pub enum Repeat {
FILE: gilrs/src/gamepad.rs
constant DEFAULT_DEADZONE (line 41) | const DEFAULT_DEADZONE: f32 = 0.1;
type Gilrs (line 133) | pub struct Gilrs {
method new (line 151) | pub fn new() -> Result<Self, Error> {
method next_event (line 158) | pub fn next_event(&mut self) -> Option<Event> {
method next_event_blocking (line 169) | pub fn next_event_blocking(&mut self, timeout: Option<Duration>) -> Op...
method next_event_inner (line 173) | fn next_event_inner(
method next_event_priv (line 209) | fn next_event_priv(
method update (line 381) | pub fn update(&mut self, event: &Event) {
method inc (line 415) | pub fn inc(&mut self) {
method counter (line 426) | pub fn counter(&self) -> u64 {
method reset_counter (line 431) | pub fn reset_counter(&mut self) {
method finish_gamepads_creation (line 435) | fn finish_gamepads_creation(&mut self) {
method gamepad (line 470) | pub fn gamepad(&self, id: GamepadId) -> Gamepad {
method connected_gamepad (line 478) | pub fn connected_gamepad(&self, id: GamepadId) -> Option<Gamepad<'_>> {
method gamepads (line 504) | pub fn gamepads(&self) -> ConnectedGamepadsIterator<'_> {
method insert_event (line 509) | pub fn insert_event(&mut self, ev: Event) {
method ff_sender (line 513) | pub(crate) fn ff_sender(&self) -> &Sender<Message> {
method set_mapping (line 551) | pub fn set_mapping<'b, O: Into<Option<&'b str>>>(
method set_mapping_strict (line 592) | pub fn set_mapping_strict<'b, O: Into<Option<&'b str>>>(
method next_ff_id (line 609) | pub(crate) fn next_ff_id(&mut self) -> usize {
type GilrsBuilder (line 621) | pub struct GilrsBuilder {
method new (line 633) | pub fn new() -> Self {
method with_default_filters (line 648) | pub fn with_default_filters(mut self, default_filters: bool) -> Self {
method add_mappings (line 655) | pub fn add_mappings(mut self, mappings: &str) -> Self {
method add_env_mappings (line 663) | pub fn add_env_mappings(mut self, env_mappings: bool) -> Self {
method add_included_mappings (line 671) | pub fn add_included_mappings(mut self, included_mappings: bool) -> Self {
method set_axis_to_btn (line 681) | pub fn set_axis_to_btn(mut self, pressed: f32, released: f32) -> Self {
method set_update_state (line 690) | pub fn set_update_state(mut self, enabled: bool) -> Self {
method build (line 697) | pub fn build(mut self) -> Result<Gilrs, Error> {
method default (line 756) | fn default() -> Self {
type ConnectedGamepadsIterator (line 762) | pub struct ConnectedGamepadsIterator<'a>(&'a Gilrs, usize);
type Item (line 765) | type Item = (GamepadId, Gamepad<'a>);
method next (line 767) | fn next(&mut self) -> Option<(GamepadId, Gamepad<'a>)> {
type Gamepad (line 789) | pub struct Gamepad<'a> {
function name (line 796) | pub fn name(&self) -> &str {
function map_name (line 806) | pub fn map_name(&self) -> Option<&str> {
function os_name (line 811) | pub fn os_name(&self) -> &str {
function uuid (line 819) | pub fn uuid(&self) -> [u8; 16] {
function vendor_id (line 824) | pub fn vendor_id(&self) -> Option<u16> {
function product_id (line 829) | pub fn product_id(&self) -> Option<u16> {
function state (line 834) | pub fn state(&self) -> &GamepadState {
function is_connected (line 839) | pub fn is_connected(&self) -> bool {
function is_pressed (line 849) | pub fn is_pressed(&self, btn: Button) -> bool {
function value (line 858) | pub fn value(&self, axis: Axis) -> f32 {
function button_data (line 867) | pub fn button_data(&self, btn: Button) -> Option<&ButtonData> {
function axis_data (line 876) | pub fn axis_data(&self, axis: Axis) -> Option<&AxisData> {
function power_info (line 881) | pub fn power_info(&self) -> PowerInfo {
function mapping_source (line 898) | pub fn mapping_source(&self) -> MappingSource {
function is_ff_supported (line 908) | pub fn is_ff_supported(&self) -> bool {
function set_listener_position (line 913) | pub fn set_listener_position<Vec3: Into<[f32; 3]>>(
function axis_or_btn_name (line 931) | pub fn axis_or_btn_name(&self, ec: Code) -> Option<AxisOrBtn> {
function button_code (line 936) | pub fn button_code(&self, btn: Button) -> Option<Code> {
function axis_code (line 941) | pub fn axis_code(&self, axis: Axis) -> Option<Code> {
function deadzone (line 946) | pub fn deadzone(&self, axis: Code) -> Option<f32> {
function id (line 961) | pub fn id(&self) -> GamepadId {
function mapping (line 965) | pub(crate) fn mapping(&self) -> &Mapping {
type GamepadData (line 971) | pub(crate) struct GamepadData {
method new (line 981) | fn new(
method map_name (line 1027) | pub fn map_name(&self) -> Option<&str> {
method is_pressed (line 1041) | pub fn is_pressed(&self, btn: Button) -> bool {
method value (line 1055) | pub fn value(&self, axis: Axis) -> f32 {
method button_data (line 1068) | pub fn button_data(&self, btn: Button) -> Option<&ButtonData> {
method axis_data (line 1078) | pub fn axis_data(&self, axis: Axis) -> Option<&AxisData> {
method axis_or_btn_name (line 1084) | pub fn axis_or_btn_name(&self, ec: Code) -> Option<AxisOrBtn> {
method button_code (line 1089) | pub fn button_code(&self, btn: Button) -> Option<Code> {
method axis_code (line 1094) | pub fn axis_code(&self, axis: Axis) -> Option<Code> {
type MappingSource (line 1101) | pub enum MappingSource {
type GamepadId (line 1117) | pub struct GamepadId(pub(crate) usize);
function from (line 1120) | fn from(x: GamepadId) -> usize {
method fmt (line 1126) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
function axis_value (line 1131) | fn axis_value(info: &AxisInfo, val: i32, axis: Axis) -> f32 {
function btn_value (line 1157) | fn btn_value(info: &AxisInfo, val: i32) -> f32 {
type Error (line 1168) | pub enum Error {
method source (line 1191) | fn source(&self) -> Option<&(dyn error::Error + 'static)> {
method fmt (line 1179) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
constant _ (line 1199) | const _: () = {
function axis_value_documented_case (line 1211) | fn axis_value_documented_case() {
function axis_value_overflow (line 1222) | fn axis_value_overflow() {
function btn_value_overflow (line 1239) | fn btn_value_overflow() {
FILE: gilrs/src/mapping/mod.rs
constant SDL_PLATFORM_NAME (line 29) | const SDL_PLATFORM_NAME: &str = "Linux";
constant SDL_PLATFORM_NAME (line 31) | const SDL_PLATFORM_NAME: &str = "Mac OS X";
constant SDL_PLATFORM_NAME (line 33) | const SDL_PLATFORM_NAME: &str = "Windows";
constant SDL_PLATFORM_NAME (line 39) | const SDL_PLATFORM_NAME: &str = "Unknown";
type Mapping (line 46) | pub struct Mapping {
method new (line 54) | pub fn new() -> Self {
method default (line 63) | pub fn default(gamepad: &gilrs_core::Gamepad) -> Self {
method name (line 173) | pub fn name(&self) -> &str {
method from_data (line 177) | pub fn from_data(
method parse_sdl_mapping (line 268) | pub fn parse_sdl_mapping(
method add_button (line 374) | fn add_button(
method add_axis (line 391) | fn add_axis(
method is_name_valid (line 408) | fn is_name_valid(name: &str) -> bool {
method map (line 412) | pub fn map(&self, code: &EvCode) -> Option<AxisOrBtn> {
method map_rev (line 416) | pub fn map_rev(&self, el: &AxisOrBtn) -> Option<EvCode> {
method is_default (line 420) | pub fn is_default(&self) -> bool {
method hats_mapped (line 426) | pub fn hats_mapped(&self) -> u8 {
type ParseSdlMappingError (line 432) | pub enum ParseSdlMappingError {
method from (line 438) | fn from(f: ParserError) -> Self {
method source (line 444) | fn source(&self) -> Option<&(dyn Error + 'static)> {
method fmt (line 454) | fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
type MappingDb (line 465) | pub struct MappingDb {
method new (line 470) | pub fn new() -> Self {
method add_included_mappings (line 476) | pub fn add_included_mappings(&mut self) {
method add_env_mappings (line 484) | pub fn add_env_mappings(&mut self) {
method insert (line 490) | pub fn insert(&mut self, s: &str) {
method get (line 510) | pub fn get(&self, uuid: Uuid) -> Option<&str> {
method len (line 514) | pub fn len(&self) -> usize {
type MappingData (line 528) | pub struct MappingData {
method new (line 535) | pub fn new() -> Self {
method button (line 543) | pub fn button(&self, idx: Button) -> Option<ev::Code> {
method axis (line 548) | pub fn axis(&self, idx: Axis) -> Option<ev::Code> {
method insert_btn (line 553) | pub fn insert_btn(&mut self, from: ev::Code, to: Button) -> Option<ev:...
method insert_axis (line 558) | pub fn insert_axis(&mut self, from: ev::Code, to: Axis) -> Option<ev::...
method remove_button (line 563) | pub fn remove_button(&mut self, idx: Button) -> Option<ev::Code> {
method remove_axis (line 568) | pub fn remove_axis(&mut self, idx: Axis) -> Option<ev::Code> {
type MappingError (line 576) | pub enum MappingError {
method fmt (line 596) | fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
constant TEST_STR (line 628) | const TEST_STR: &str = "03000000260900008888000000010001,GameCube {WiseG...
constant BUTTONS (line 633) | const BUTTONS: [EvCode; 15] = [
constant AXES (line 651) | const AXES: [EvCode; 12] = [
function mapping (line 667) | fn mapping() {
function from_data (line 672) | fn from_data() {
function with_mappings (line 717) | fn with_mappings() {
FILE: gilrs/src/mapping/parser.rs
type Parser (line 82) | pub struct Parser<'a> {
function new (line 89) | pub fn new(mapping: &'a str) -> Self {
function next_token (line 97) | pub fn next_token(&mut self) -> Option<Result<Token<'_>, Error>> {
function parse_uuid (line 110) | fn parse_uuid(&mut self) -> Result<Token<'_>, Error> {
function parse_name (line 135) | fn parse_name(&mut self) -> Result<Token<'_>, Error> {
function parse_key_val (line 145) | fn parse_key_val(&mut self) -> Result<Token<'_>, Error> {
function next_comma_or_end (line 278) | fn next_comma_or_end(&self) -> usize {
type Token (line 287) | pub enum Token<'a> {
type AxisRange (line 318) | pub enum AxisRange {
type State (line 325) | enum State {
type Error (line 333) | pub struct Error {
method new (line 339) | pub fn new(kind: ErrorKind, position: usize) -> Self {
method kind (line 343) | pub fn kind(&self) -> &ErrorKind {
type ErrorKind (line 350) | pub enum ErrorKind {
method fmt (line 364) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
function test_all_sdl_mappings_for_parse_errors (line 386) | fn test_all_sdl_mappings_for_parse_errors() {
FILE: gilrs/src/utils.rs
function ceil_div (line 11) | pub fn ceil_div(a: u32, b: u32) -> u32 {
function clamp (line 19) | pub fn clamp(x: f32, min: f32, max: f32) -> f32 {
function t_clamp (line 44) | fn t_clamp() {
Condensed preview — 64 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (457K chars).
[
{
"path": ".gitignore",
"chars": 30,
"preview": "target\nCargo.lock\n!.gitignore\n"
},
{
"path": ".gitlab-ci.yml",
"chars": 1602,
"preview": "image: \"rust:1.80\"\n\nvariables:\n CARGO_HOME: $CI_PROJECT_DIR/cargo\n GIT_SUBMODULE_STRATEGY: normal\n\nprepare:lockfile:\n "
},
{
"path": ".gitmodules",
"chars": 129,
"preview": "[submodule \"SDL_GameControllerDB\"]\n\tpath = gilrs/SDL_GameControllerDB\n\turl = https://github.com/gabomdq/SDL_GameControll"
},
{
"path": "Cargo.toml",
"chars": 53,
"preview": "[workspace]\nresolver = \"2\"\nmembers = [\n \"gilrs\"\n]\n"
},
{
"path": "LICENSE-APACHE",
"chars": 10847,
"preview": " Apache License\n Version 2.0, January 2004\n http"
},
{
"path": "LICENSE-MIT",
"chars": 1091,
"preview": "Copyright (c) 2016-2018 Mateusz Sieczko and other GilRs\nDevelopers\n\nPermission is hereby granted, free of charge, to any"
},
{
"path": "README.md",
"chars": 4598,
"preview": "GilRs - Game Input Library for Rust\n===================================\n\n[.\n\nv0.11.0 - 2024-09-15\n----------\n\n##"
},
{
"path": "gilrs/Cargo.toml",
"chars": 1300,
"preview": "[package]\nname = \"gilrs\"\nversion = \"0.11.0\"\nauthors = [\"Arvamer <arvamer@gmail.com>\"]\nlicense = \"Apache-2.0/MIT\"\nexclude"
},
{
"path": "gilrs/build.rs",
"chars": 2765,
"preview": "//! This build script takes the gamecontrollerdb.txt from the SDL repo and removes any\n//! mappings that aren't for the "
},
{
"path": "gilrs/examples/ev.rs",
"chars": 1466,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs/examples/ff.rs",
"chars": 1586,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs/examples/ff_pos.rs",
"chars": 6418,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs/examples/gamepad_info.rs",
"chars": 2177,
"preview": "use gilrs::{Axis, Button, Gilrs};\nuse uuid::Uuid;\n\nfn main() {\n env_logger::init();\n let gilrs = Gilrs::new().unwr"
},
{
"path": "gilrs/examples/gui.rs",
"chars": 14952,
"preview": "#![cfg_attr(not(debug_assertions), windows_subsystem = \"windows\")] // hide console window on Windows in release\n\nuse efr"
},
{
"path": "gilrs/examples/wasm/.gitignore",
"chars": 8,
"preview": "target/\n"
},
{
"path": "gilrs/examples/wasm/README.md",
"chars": 783,
"preview": "# Wasm Example\n\nThese are instructions for running the GUI example in your web browser using Wasm.\n\nCurrently only the G"
},
{
"path": "gilrs/examples/wasm/index.html",
"chars": 2211,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\"/>\n <title>Gilrs Example</title>\n "
},
{
"path": "gilrs/examples/wasm/wasm_gui.ps1",
"chars": 853,
"preview": "### This script can be used instead of the \"Build and Run\" step in `./gilrs/examples/wasm/README.md`.\n### Useful for gil"
},
{
"path": "gilrs/examples/wasm/wasm_gui.sh",
"chars": 879,
"preview": "#!/usr/bin/env bash\n\n### This script can be used instead of the \"Build and Run\" step in `./gilrs/examples/wasm/README.md"
},
{
"path": "gilrs/src/constants.rs",
"chars": 1267,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs/src/ev/filter.rs",
"chars": 19554,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs/src/ev/mod.rs",
"chars": 8526,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs/src/ev/state.rs",
"chars": 6014,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs/src/ff/base_effect.rs",
"chars": 4633,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs/src/ff/effect_source.rs",
"chars": 11749,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs/src/ff/mod.rs",
"chars": 14018,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs/src/ff/server.rs",
"chars": 9382,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs/src/ff/time.rs",
"chars": 2656,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs/src/gamepad.rs",
"chars": 42977,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs/src/lib.rs",
"chars": 5591,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs/src/mapping/mod.rs",
"chars": 25170,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs/src/mapping/parser.rs",
"chars": 11717,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs/src/utils.rs",
"chars": 1091,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs-core/CHANGELOG.md",
"chars": 6201,
"preview": "Change Log\n==========\n\nv0.6.6 - 2025-09-28\n----------\n\n### Fixed\n\n- Fixed mappings on macOS.\n\nv0.6.5 - 2025-09-21\n------"
},
{
"path": "gilrs-core/Cargo.toml",
"chars": 2051,
"preview": "[package]\nname = \"gilrs-core\"\nversion = \"0.6.6\"\nauthors = [\"Mateusz Sieczko <arvamer@gmail.com>\"]\nlicense = \"Apache-2.0/"
},
{
"path": "gilrs-core/README.md",
"chars": 1120,
"preview": "GilRs Core\n==========\n\n[](https://g"
},
{
"path": "gilrs-core/examples/ev_core.rs",
"chars": 232,
"preview": "use gilrs_core::Gilrs;\n\nfn main() {\n env_logger::init();\n\n let mut gilrs = Gilrs::new().unwrap();\n loop {\n "
},
{
"path": "gilrs-core/src/lib.rs",
"chars": 10868,
"preview": "#[macro_use]\nextern crate log;\n\nuse std::fmt;\nuse std::fmt::Display;\nuse std::fmt::Formatter;\n\nuse std::error;\nuse std::"
},
{
"path": "gilrs-core/src/platform/default/ff.rs",
"chars": 630,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs-core/src/platform/default/gamepad.rs",
"chars": 3954,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs-core/src/platform/default/mod.rs",
"chars": 581,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs-core/src/platform/linux/ff.rs",
"chars": 3679,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs-core/src/platform/linux/gamepad.rs",
"chars": 41598,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs-core/src/platform/linux/ioctl.rs",
"chars": 4251,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs-core/src/platform/linux/mod.rs",
"chars": 589,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs-core/src/platform/linux/udev.rs",
"chars": 6550,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs-core/src/platform/macos/ff.rs",
"chars": 633,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs-core/src/platform/macos/gamepad.rs",
"chars": 28736,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs-core/src/platform/macos/io_kit.rs",
"chars": 12418,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs-core/src/platform/macos/mod.rs",
"chars": 592,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs-core/src/platform/mod.rs",
"chars": 1888,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs-core/src/platform/wasm/ff.rs",
"chars": 532,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs-core/src/platform/wasm/gamepad.rs",
"chars": 15252,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs-core/src/platform/wasm/mod.rs",
"chars": 170,
"preview": "mod ff;\nmod gamepad;\n\npub use self::ff::Device as FfDevice;\npub use self::gamepad::{native_ev_codes, EvCode, Gamepad, Gi"
},
{
"path": "gilrs-core/src/platform/windows_wgi/ff.rs",
"chars": 1331,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs-core/src/platform/windows_wgi/gamepad.rs",
"chars": 34137,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs-core/src/platform/windows_wgi/mod.rs",
"chars": 533,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs-core/src/platform/windows_xinput/ff.rs",
"chars": 1333,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs-core/src/platform/windows_xinput/gamepad.rs",
"chars": 21936,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs-core/src/platform/windows_xinput/mod.rs",
"chars": 534,
"preview": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, "
},
{
"path": "gilrs-core/src/utils.rs",
"chars": 509,
"preview": "use std::time::SystemTime;\n\n/// Returns true if nth bit in array is 1.\n#[allow(dead_code)]\npub(crate) fn test_bit(n: u16"
},
{
"path": "rustfmt.toml",
"chars": 160,
"preview": "unstable_features = true\ncondense_wildcard_suffixes = true\nerror_on_line_overflow = false\nreorder_imports = true\nuse_try"
}
]
About this extraction
This page contains the full source code of the Arvamer/gilrs GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 64 files (427.7 KB), approximately 106.6k tokens, and a symbol index with 1018 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.