[
  {
    "path": ".gitignore",
    "content": "target\nCargo.lock\n!.gitignore\n"
  },
  {
    "path": ".gitlab-ci.yml",
    "content": "image: \"rust:1.80\"\n\nvariables:\n  CARGO_HOME: $CI_PROJECT_DIR/cargo\n  GIT_SUBMODULE_STRATEGY: normal\n\nprepare:lockfile:\n  stage: .pre\n  image: \"rust:1\"\n  script:\n    - CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS=fallback cargo generate-lockfile\n  artifacts:\n    paths:\n      - Cargo.lock\n\ntest:x86_64-unknown-linux-gnu:\n  stage: test\n  before_script:\n    - apt-get update -yqq && apt-get install -yqq libudev-dev\n    # eframe dependencies for testing gui example\n    - apt-get install -yqq libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libspeechd-dev libxkbcommon-dev libssl-dev\n    - rustc -Vv && cargo -Vv\n  script:\n    - cargo test --verbose --all --features serde-serialize\n\ntest:i686-unknown-linux-gnu:\n  stage: test\n  variables:\n    PKG_CONFIG_ALLOW_CROSS: \"1\"\n  before_script:\n    - dpkg --add-architecture i386\n    - apt-get update -yqq && apt-get install -yqq gcc-multilib libudev-dev libudev1:i386 libudev-dev:i386\n    - rustc -Vv && cargo -Vv\n    - rustup target add i686-unknown-linux-gnu\n  script:\n    - cargo test --verbose --target=i686-unknown-linux-gnu --lib --features serde-serialize\n\nbuild:wasm32-unknown-unknown:\n  image: \"rust:1.80\"\n  stage: test\n  before_script:\n    - rustc -Vv && cargo -Vv\n    - rustup target add wasm32-unknown-unknown\n  script:\n    - cargo test --no-run --target wasm32-unknown-unknown --all --features serde-serialize\n\ncheck:x86_64-apple-darwin:\n  stage: test\n  before_script:\n    - rustc -Vv && cargo -Vv\n    - rustup target add x86_64-apple-darwin\n  script:\n    - cargo check --target=x86_64-apple-darwin --verbose --all --features serde-serialize\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"SDL_GameControllerDB\"]\n\tpath = gilrs/SDL_GameControllerDB\n\turl = https://github.com/gabomdq/SDL_GameControllerDB.git\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[workspace]\nresolver = \"2\"\nmembers = [\n    \"gilrs\"\n]\n"
  },
  {
    "path": "LICENSE-APACHE",
    "content": "                              Apache License\n                        Version 2.0, January 2004\n                     http://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n   \"License\" shall mean the terms and conditions for use, reproduction,\n   and distribution as defined by Sections 1 through 9 of this document.\n\n   \"Licensor\" shall mean the copyright owner or entity authorized by\n   the copyright owner that is granting the License.\n\n   \"Legal Entity\" shall mean the union of the acting entity and all\n   other entities that control, are controlled by, or are under common\n   control with that entity. For the purposes of this definition,\n   \"control\" means (i) the power, direct or indirect, to cause the\n   direction or management of such entity, whether by contract or\n   otherwise, or (ii) ownership of fifty percent (50%) or more of the\n   outstanding shares, or (iii) beneficial ownership of such entity.\n\n   \"You\" (or \"Your\") shall mean an individual or Legal Entity\n   exercising permissions granted by this License.\n\n   \"Source\" form shall mean the preferred form for making modifications,\n   including but not limited to software source code, documentation\n   source, and configuration files.\n\n   \"Object\" form shall mean any form resulting from mechanical\n   transformation or translation of a Source form, including but\n   not limited to compiled object code, generated documentation,\n   and conversions to other media types.\n\n   \"Work\" shall mean the work of authorship, whether in Source or\n   Object form, made available under the License, as indicated by a\n   copyright notice that is included in or attached to the work\n   (an example is provided in the Appendix below).\n\n   \"Derivative Works\" shall mean any work, whether in Source or Object\n   form, that is based on (or derived from) the Work and for which the\n   editorial revisions, annotations, elaborations, or other modifications\n   represent, as a whole, an original work of authorship. For the purposes\n   of this License, Derivative Works shall not include works that remain\n   separable from, or merely link (or bind by name) to the interfaces of,\n   the Work and Derivative Works thereof.\n\n   \"Contribution\" shall mean any work of authorship, including\n   the original version of the Work and any modifications or additions\n   to that Work or Derivative Works thereof, that is intentionally\n   submitted to Licensor for inclusion in the Work by the copyright owner\n   or by an individual or Legal Entity authorized to submit on behalf of\n   the copyright owner. For the purposes of this definition, \"submitted\"\n   means any form of electronic, verbal, or written communication sent\n   to the Licensor or its representatives, including but not limited to\n   communication on electronic mailing lists, source code control systems,\n   and issue tracking systems that are managed by, or on behalf of, the\n   Licensor for the purpose of discussing and improving the Work, but\n   excluding communication that is conspicuously marked or otherwise\n   designated in writing by the copyright owner as \"Not a Contribution.\"\n\n   \"Contributor\" shall mean Licensor and any individual or Legal Entity\n   on behalf of whom a Contribution has been received by Licensor and\n   subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   copyright license to reproduce, prepare Derivative Works of,\n   publicly display, publicly perform, sublicense, and distribute the\n   Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   (except as stated in this section) patent license to make, have made,\n   use, offer to sell, sell, import, and otherwise transfer the Work,\n   where such license applies only to those patent claims licensable\n   by such Contributor that are necessarily infringed by their\n   Contribution(s) alone or by combination of their Contribution(s)\n   with the Work to which such Contribution(s) was submitted. If You\n   institute patent litigation against any entity (including a\n   cross-claim or counterclaim in a lawsuit) alleging that the Work\n   or a Contribution incorporated within the Work constitutes direct\n   or contributory patent infringement, then any patent licenses\n   granted to You under this License for that Work shall terminate\n   as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the\n   Work or Derivative Works thereof in any medium, with or without\n   modifications, and in Source or Object form, provided that You\n   meet the following conditions:\n\n   (a) You must give any other recipients of the Work or\n       Derivative Works a copy of this License; and\n\n   (b) You must cause any modified files to carry prominent notices\n       stating that You changed the files; and\n\n   (c) You must retain, in the Source form of any Derivative Works\n       that You distribute, all copyright, patent, trademark, and\n       attribution notices from the Source form of the Work,\n       excluding those notices that do not pertain to any part of\n       the Derivative Works; and\n\n   (d) If the Work includes a \"NOTICE\" text file as part of its\n       distribution, then any Derivative Works that You distribute must\n       include a readable copy of the attribution notices contained\n       within such NOTICE file, excluding those notices that do not\n       pertain to any part of the Derivative Works, in at least one\n       of the following places: within a NOTICE text file distributed\n       as part of the Derivative Works; within the Source form or\n       documentation, if provided along with the Derivative Works; or,\n       within a display generated by the Derivative Works, if and\n       wherever such third-party notices normally appear. The contents\n       of the NOTICE file are for informational purposes only and\n       do not modify the License. You may add Your own attribution\n       notices within Derivative Works that You distribute, alongside\n       or as an addendum to the NOTICE text from the Work, provided\n       that such additional attribution notices cannot be construed\n       as modifying the License.\n\n   You may add Your own copyright statement to Your modifications and\n   may provide additional or different license terms and conditions\n   for use, reproduction, or distribution of Your modifications, or\n   for any such Derivative Works as a whole, provided Your use,\n   reproduction, and distribution of the Work otherwise complies with\n   the conditions stated in this License.\n\n5. Submission of Contributions. Unless You explicitly state otherwise,\n   any Contribution intentionally submitted for inclusion in the Work\n   by You to the Licensor shall be under the terms and conditions of\n   this License, without any additional terms or conditions.\n   Notwithstanding the above, nothing herein shall supersede or modify\n   the terms of any separate license agreement you may have executed\n   with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade\n   names, trademarks, service marks, or product names of the Licensor,\n   except as required for reasonable and customary use in describing the\n   origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or\n   agreed to in writing, Licensor provides the Work (and each\n   Contributor provides its Contributions) on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n   implied, including, without limitation, any warranties or conditions\n   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n   PARTICULAR PURPOSE. You are solely responsible for determining the\n   appropriateness of using or redistributing the Work and assume any\n   risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory,\n   whether in tort (including negligence), contract, or otherwise,\n   unless required by applicable law (such as deliberate and grossly\n   negligent acts) or agreed to in writing, shall any Contributor be\n   liable to You for damages, including any direct, indirect, special,\n   incidental, or consequential damages of any character arising as a\n   result of this License or out of the use or inability to use the\n   Work (including but not limited to damages for loss of goodwill,\n   work stoppage, computer failure or malfunction, or any and all\n   other commercial damages or losses), even if such Contributor\n   has been advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability. While redistributing\n   the Work or Derivative Works thereof, You may choose to offer,\n   and charge a fee for, acceptance of support, warranty, indemnity,\n   or other liability obligations and/or rights consistent with this\n   License. However, in accepting such obligations, You may act only\n   on Your own behalf and on Your sole responsibility, not on behalf\n   of any other Contributor, and only if You agree to indemnify,\n   defend, and hold each Contributor harmless for any liability\n   incurred by, or claims asserted against, such Contributor by reason\n   of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work.\n\n   To apply the Apache License to your work, attach the following\n   boilerplate notice, with the fields enclosed by brackets \"[]\"\n   replaced with your own identifying information. (Don't include\n   the brackets!)  The text should be enclosed in the appropriate\n   comment syntax for the file format. We also recommend that a\n   file or class name and description of purpose be included on the\n   same \"printed page\" as the copyright notice for easier\n   identification within third-party archives.\n\nCopyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n\thttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
  },
  {
    "path": "LICENSE-MIT",
    "content": "Copyright (c) 2016-2018 Mateusz Sieczko and other GilRs\nDevelopers\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "GilRs - Game Input Library for Rust\n===================================\n\n[![pipeline status](https://gitlab.com/gilrs-project/gilrs/badges/master/pipeline.svg)](https://gitlab.com/gilrs-project/gilrs/commits/master)\n[![Crates.io](https://img.shields.io/crates/v/gilrs.svg)](https://crates.io/crates/gilrs)\n[![Documentation](https://docs.rs/gilrs/badge.svg)](https://docs.rs/gilrs/)\n[![Minimum rustc version](https://img.shields.io/badge/rustc-1.80.0+-yellow.svg)](https://gitlab.com/gilrs-project/gilrs)\n\nGilRs abstract platform specific APIs to provide unified interfaces for working with gamepads.\n\nMain features:\n\n- Unified gamepad layout—buttons and axes are represented by familiar names\n- Support for SDL2 mappings including `SDL_GAMECONTROLLERCONFIG` environment\n  variable which Steam uses\n- Hotplugging—GilRs will try to assign new IDs for new gamepads and reuse the same\n  ID for gamepads which reconnected\n- Force feedback (rumble)\n- Power information (is gamepad wired, current battery status)\n\nThe project's main repository [is on GitLab](https://gitlab.com/gilrs-project/gilrs)\nalthough there is also a [GitHub mirror](https://github.com/Arvamer/gilrs).\nPlease use GitLab's issue tracker and merge requests.\n\nThis repository contains submodule; after you clone it, don't forget to run\n`git submodule init; git submodule update` (or clone with `--recursive` flag)\nor you will get compile errors.\n\nExample\n-------\n\n```toml\n[dependencies]\ngilrs = \"0.11.0\"\n```\n\n```rust\nuse gilrs::{Gilrs, Button, Event};\n\nlet mut gilrs = Gilrs::new().unwrap();\n\n// Iterate over all connected gamepads\nfor (_id, gamepad) in gilrs.gamepads() {\n    println!(\"{} is {:?}\", gamepad.name(), gamepad.power_info());\n}\n\nlet mut active_gamepad = None;\n\nloop {\n    // Examine new events\n    while let Some(Event { id, event, time, .. }) = gilrs.next_event() {\n        println!(\"{:?} New event from {}: {:?}\", time, id, event);\n        active_gamepad = Some(id);\n    }\n\n    // You can also use cached gamepad state\n    if let Some(gamepad) = active_gamepad.map(|id| gilrs.gamepad(id)) {\n        if gamepad.is_pressed(Button::South) {\n            println!(\"Button South is pressed (XBox - A, PS - X)\");\n        }\n    }\n}\n```\n\nSupported features\n------------------\n\n|                  | Input | Hotplugging | Force feedback |\n|------------------|:-----:|:-----------:|:--------------:|\n| Linux/BSD (evdev)|   ✓   |      ✓      |        ✓       |\n| Windows          |   ✓   |      ✓      |        ✓       |\n| OS X             |   ✓   |      ✓      |        ✕       |\n| Wasm             |   ✓   |      ✓      |       n/a      |\n| Android          |   ✕   |      ✕      |        ✕       |\n\nPlatform specific notes\n======================\n\nLinux/BSD (evdev)\n-----\n\nWith evdev, GilRs read (and write, in case of force feedback) directly from appropriate\n`/dev/input/event*` file. This mean that user have to have read and write access to this file.\nOn most distros it shouldn't be a problem, but if it is, you will have to create udev rule.\nOn FreeBSD generic HID gamepads use hgame(4) and special use Linux driver via `webcamd`.\n\nTo build GilRs, you will need pkg-config and libudev .pc file. On some distributions this file\nis packaged in separate archive (e.g., `libudev-dev` in Debian, `libudev-devd` in FreeBSD).\n\nWindows\n-----\n\nWindows defaults to using Windows Gaming Input instead of XInput. If you need to use XInput you\ncan disable the `wgi` feature (it's enabled by default) and enable the `xinput` feature.\n\nWindows Gaming Input requires an in focus window to be associated with the process to receive\nevents. You can still switch back to using xInput by turning off default features and enabling\nthe xinput feature.\n\nNote: Some (Older?) devices may still report inputs without a window but this is not the case\nfor all devices so if you are writing a terminal based game, use the xinput feature instead.\n\nWasm\n-----\n\nWasm implementation uses stdweb, or wasm-bindgen with the wasm-bindgen feature.\nFor stdweb, you will need [cargo-web](https://github.com/koute/cargo-web) to build gilrs for\nwasm32-unknown-unknown. For wasm-bindgen, you will need the wasm-bindgen cli or a tool like\n[wasm-pack](https://rustwasm.github.io/wasm-pack/installer/).\nUnlike other platforms, events are only generated when you call `Gilrs::next_event()`.\n\nSee [`./gilrs/examples/wasm/README.md`](./gilrs/examples/wasm/README.md) for running the examples using Wasm.\n\nLicense\n=======\n\nThis project is licensed under the terms of both the Apache License (Version 2.0) and the MIT\nlicense. See LICENSE-APACHE and LICENSE-MIT for details.\n"
  },
  {
    "path": "appveyor.yml",
    "content": "# Based on the \"trust\" template v0.1.1\n# https://github.com/japaric/trust/tree/v0.1.1\n\nimage:\n  - Visual Studio 2022\n\nenvironment:\n  global:\n    RUST_VERSION: 1.80.0\n    CRATE_NAME: gilrs\n\n  matrix:\n    - TARGET: x86_64-pc-windows-msvc\n\ninstall:\n  - ps: >-\n      If ($Env:TARGET -eq 'x86_64-pc-windows-gnu') {\n        $Env:PATH += ';C:\\msys64\\mingw64\\bin'\n      } ElseIf ($Env:TARGET -eq 'i686-pc-windows-gnu') {\n        $Env:PATH += ';C:\\msys64\\mingw32\\bin'\n      }\n  - curl -sSf -o rustup-init.exe https://win.rustup.rs/\n  - rustup-init.exe -y --default-host %TARGET% --default-toolchain %RUST_VERSION%\n  - set PATH=%PATH%;C:\\Users\\appveyor\\.cargo\\bin\n  - rustup toolchain install stable\n  - rustc -Vv\n  - cargo -V\n  - git submodule update --init --recursive\n  - set CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS=fallback\n  - cargo +stable generate-lockfile\n  - set CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS=\n\ntest_script:\n  - cargo test --verbose --target %TARGET%\n\ncache:\n  - C:\\Users\\appveyor\\.cargo\\registry\n  - target\n\n# Building is done in the test phase, so we disable Appveyor's build phase.\nbuild: false\n"
  },
  {
    "path": "gilrs/CHANGELOG.md",
    "content": "Change Log\n==========\n\nSee also [gilrs-core changelog](../gilrs-core/CHANGELOG.md).\n\nv0.11.0 - 2024-09-15\n----------\n\n### Breaking changes\n\n- Mark Error enums, `EventType` and `Event` as `non_exhaustive`\n\n### Added\n\n- Added `EventType::ForceFeedbackEffectCompleted`\n\n### Changed\n\n- Minimal supported Rust version is now 1.73\n- Updated dependencies\n- Updated bundled mappings\n\n### Fixed\n\n- Fixed potential overflow in `btn_value`\n\nv0.10.6 - 2024-03-16\n----------\n\n### Fixed\n\n- `axis_dpad_to_button` filter will now properly generate release event when\n  axis value change from -1 to 1 (or 1 to -1) while skipping 0.\n\nv0.10.5 - 2024-03-06\n----------\n\n### Added\n\n- Added `vendor_id()` and `product_id()` to `Gamepad`.\n\nv0.10.4 - 2023-12-03\n----------\n\n### Fixed\n\n- Fixed `Gilrs::set_mapping*` returning `MappingError::NotConnected` for\n  connected gamepads.\n- Fix not setting other axis to 0 when in deadzone range.\n\nv0.10.3 - 2023-11-11\n----------\n\n### Changed\n\n- All thread spawned by gilrs are now named. (!102)\n- MSRV is now 1.65.\n\nv0.10.2 - 2023-04-23\n----------\n\n### Added\n\n- `Gilrs::next_event_blocking()`\n\n### Fixed\n\n- Parse more SDL specific buttons in mappings\n- Recognize \"xinput\" UUID in mappings\n- Parse axis ranges in button mappings\n\nv0.10.1 - 2022-11-13\n----------\n\n### Added\n\n- Supporting files and documentation for running the GUI example using Wasm in\n  a browser. See [examples/wasm/README.md](./examples/wasm/README.md)\n\n### Changed\n\n- Bundled SDL mappings are now filtered by platform, reducing binary size.\n\n### Fixed\n\n- GUI example crash when the current platform does not support force feedback.\n\nv0.10.0 - 2022-11-06\n--------------------\n\n### Changed\n\n- Windows now defaults to using Windows Gaming Input instead of xinput.\n\n  If you need to use xInput you can disable the `wgi` feature (It's enabled by\n  default) and enable the `xinput` feature.\n  ``` toml\n  gilrs = {version = \"0.10.0\", default-features = false, features = [\"wgi\"]}\n  ```\n- Apps on Windows will now require a focused window to receive inputs by\n  default.\n\n  This is a limitation of Windows Gaming Input. It requires an in focus Window\n  be associated with the process to receive events. You can still switch back\n  to using xInput by turning off default features and enabling the `xinput`\n  feature.\n\n  Note: Some (Older?) devices may still report inputs without a window but this\n  is not the case for all devices so if you are writing a terminal based game,\n  use the `xinput` feature instead.\n\n- Minimal supported rust version is now 1.64.\n\nv0.9.0 - 2022-05-22\n-------------------\n\n### Changed\n\n- wasm: web-sys/wasm-bindgen is now used by default, dependency on stdweb\n  and `wasm-bindgen` feature are removed.\n- Minimal supported rust version is now 1.56.\n- Changed `impl Into\\<usize\\> for GamepadId`\n  to `impl From\\<GamepadId\\> for usize`\n\n### Fixed\n\n- wasm: `next_event()` no longer panic if `getGamepads()` is not available.\n\nv0.8.2 - 2021-12-30\n-------------------\n\n### Changed\n\n- Minimal supported rust version is now 1.47\n- `axis_dpad_to_btn` now also emits `ButtonChanged` events\n\n### Fixed\n\n- Fixed overflow when calculating axis value and min/max range was\n  i32::MIN/MAX (@wathiede)\n\nv0.8.1 - 2021-03-30\n-------------------\n\n### Changed\n\n- Updated bundled mappings\n\nv0.8.0 - 2020-10-09\n-------------------\n\n### Added\n\n- `Jitter`, `Repeat`, `GilrsBuilder`, and `Mapping` now implement `Default`.\n- Errors now implement `source()`.\n- `Code` now implements `Deserialize` and `Serialize` (@theunkn0wn1).\n- Dpad is now supported on macOS (@cleancut).\n\n### Changed\n\n- Minimal supported version is now 1.40\n- Non exhaustive enums now use `#[non_exhaustive]` instead of hidden variant.\n- Renamed cargo feature `serde` to `serde-serialize`.\n- Improved conversion of axis value to float. Values like 127 (when axis range\n  is 0-255) will now be correctly converted to 0.0.\n\n### Removed\n\n- Errors now longer implement deprecated methods (`source()`\n  and `description()`).\n\nv0.7.4 - 2020-02-06\n-------------------\n\n### Added\n\n- Added method to stop playing force feedback effect. (@photex)\n\n### Fixed\n\n- Fixed bug that caused forced feedback effects to never stop. (@photex)\n\nv0.7.3 - 2019-11-30\n-------------------\n\n### Added\n\n- Added support for serialization and deserialization for `Button`, `Axis`\n  and `AxisOrButton` with optional `serde` feature (@aleksijuvani).\n\n### Fixed\n\n- Fixed defaults mappings containing elements that gamepad doesn't have.\n  This also fixes state not working for `LeftTrigger`  button on Windows.\n\nv0.7.2 - 2019-08-06\n-------------------\n\n### Fixed\n\n- Fixed loading mappings for wrong platform\n\nv0.7.1 - 2019-03-04\n-------------------\n\n### Fixed\n\n- Compilation on macOS.\n- xinput: Calling `set_ff_state()` on devices that were never connected.\n- `GamepadId` was not reexported from private module.\n\nv0.7.0 - 2019-02-21\n-------------------\n\n### Added\n\n- `GamepadId`\n- `Gilrs::gamepad(id)`. This function is replacement\n  for `Index` operator and can return disconnected gamepads.\n- Initial support for macOS (@jtakakura). There are still some functionality\n  missing, check related issues in #58.\n- Wasm support, using stdweb (@ryanisaacg).\n\n### Changed\n\n- Change `Gamepad::uuid -> Uuid` to `Gamepad::uuid -> [u8; 16]`\n- `gilrs` now uses `gilrs-core` crate as backend. Because of it,\n  there are some breaking changes to API.\n- Functions that returned `&Gamepad` now return `Gamepad<'_>` proxy object.\n- Renamed `Gilrs::get(id)` to `Gilrs::connected_gamepad(id)`.\n- Moved `Gamepad::set_mapping{,_strict}()` to `Gilrs`. These functions now\n  also take gamepad id as additional argument.\n- Minimal supported version is now 1.31.1. The crate can still be build with\n  older rustc, but it may change during next patch release.\n- Instead using `usize` for gamepad ID, `GamepadId` is now used.\n- Updated bundled SDL_GameControllerDB.\n\n### Removed\n\n- All functions that returned `&mut Gamepad`.\n- `Gilrs` no longer implements `Index` and `IndexMut` operators. Use\n  `Gilrs::gamepad(id)` instead.\n- `Gamepad::status()` and `Status` enum. `Gamepad::is_connected()` is\n  now sufficient to determine status of gamepad.\n\n### Fixed\n\n- xinput: Incorrect gamepad ID when more than one gamepad is connected\n  (@DTibbs).\n- Deadzone filter no longer emits additional events. This resulted in emitting\n  more events until values normalized on some, often unrelated (like 0 for axis\n  around 0.5), value.\n- Mappings from environment variable had lower priority than bundled mappings.\n\nv0.6.1 - 2018-07-18\n-------------------\n\n### Added\n\n- `ev::Code::into_u32()` (@rukai).\n- `ev::{Button, Axis, AxisOrBtn}` now implements `Hash` (@sheath).\n\n### Changed\n\n- The URL of repository has changed to https://gitlab.com/gilrs-project/gilrs\n- Updated bundled SDL_GameControllerDB.\n\n### Fixed\n\n- Various fixes to logging at incorrect log level. Thanks to @fuggles for\n  locating and reporting these issues.\n- Possible panic in `Repeat` filter.\n- `Axis::DPadY` was inverted on Linux.\n\nv0.6.0 - 2018-02-11\n-------------------\n\n### Added\n\n- Support for parsing SLD 2.0.6 mappings.\n- `ButtonChanged` event. It contains value in range [0.0, 1.0].\n- `GilrsBuilder::set_axis_to_btn()`. It allow to customize on which values\n  `ButtonePressed` and `ButtonReleased` are emitted.\n- `GilrsBuilder::set_update_state` which control whether gamepad state should\n  be updated automatically.\n- `ButtonState::value()`.\n- `Mapping::insert_{btn,axis}()`.\n- `Gampead::os_name()` and `Gamepad::map_name()`. (@rukai)\n- `GilrsBuilder::add_env_mappings()`\n  and `GilrsBuilder::add_included_mappings()`,\n  allow to configure whether to load mappings from `SDL_GAMECONTROLLERCONFIG`\n  env\n  and bundled mappings. (@rukai)\n- `Gilrs::insert_event()`.\n- `Axis::second_axis()` – returns the other axis of gamepad element. For\n  example,\n  this function will return `LeftStickX` for `LeftStickY`.\n\n### Removed\n\n- `Mapping` no longer implements `Index` and `IndexMut` operators. Use\n  `Mapping::insert_{btn,axis}()` methods to add new mappings.\n- `Axis::{LeftTrigger, LeftTrigger2, RightTrigger, RightTrigger2}`. All events\n  with these are now button events. `ButtonChanged` event contains value.\n- `Gilrs::gamepad()` and `Gilrs::gamepad_mut()` – use `Index` operator instead.\n\n### Changed\n\n- Gilrs now require Rust 1.20.0 or newer.\n- Updated bundled mappings.\n- Renamed `Filter::filter` to `Filter::filter_ev` because RFC 2124 added\n  `filter` method to `Option` (our `Filter` is implemented\n  for `Option<Event>`).\n- `Gamepad::deadzone()` now returns `Option<f32>` instead of `f32`.\n- All axis events are now in range [-1.0, 1.0].\n- `NativeEvCode` is replaced by `ev::Code`, a strongly typed struct that also\n  distinguish between axes and buttons.\n- You can now create mappings from any axis to any button.\n- `State` now tracks floating-point value of buttons.\n- `State::value()` can now be used to also examine value of buttons.\n- By default, gamepad state is updated automatically. If you customize event\n  filters, you can disable this behaviour\n  using `GilrsBuilder::set_update_state`.\n- `Gilrs::new()` and `GilrsBuilder::build()` now returns `Result`. Dummy\n  context\n  can still be used, but only if result of failure is unsupported platform.\n- Renamed `Gilrs::connected_gamepad()` and `Gilrs::connected_gamepad_mut()` to\n  `get()` and `get_mut()`.\n- `Filter` and `FilterFn` now borrows `Gilrs` mutably.\n- Windows: Gamepads are now named \"Xbox Controller\" instead of \"XInput\n  Controller\".\n  (@rukai)\n\n### Fixed\n\n- Incorrect ranges for some axes.\n- Deadzone filter should no longer produce values outside of allowed range.\n- When calculating deadzone, the value of second axis is no longer ignored.\n  This fixes situation, when sometimes axis would stay on value small to 0.0,\n  when it should be 0.0 instead.\n- Deadzone threshold was half of what it should be.\n- Linux: Fixed axis value normalization if neither minimal value is 0 nor\n  midpoint is 0. (@scottpleb)\n- Linux: Ensure that axis values are clamped after normalization. (@scottpleb)\n- Linux: Compilation error on architectures with `c_char = u8`.\n\nv0.5.0 - 2017-09-24\n-------------------\n\n### Added\n\n- `Mapping::remove_button()` and `Mapping::remove_axis()`.\n- `GilrsBuilder` for customizing how `Gilrs` is created.\n- Event filters. See `ev::filter` module for more info.\n- `Gilrs::next_event()` - use it with `while let` loop in your event loop.\n  This allow to avoid borrow checker problems that `EventIterator` caused.\n- New event – `Dropped`. Used by filters to indicate that you should ignore\n  this event.\n- New event – `ButtonRepeated`. Can be emitted by `Repeat` filter.\n- `Axis::{DPadX, DPadY}`\n- `Gamepad::{button_name, axis_name, button_code, axis_code}` functions for\n  accessing mapping data.\n- `Gamepad::axis_data, button_data` – part of new extended gamepad state.\n- `Gamepad::id()` – returns gamepad ID.\n- `Gilrs::update, inc, counter, reset_counter` – part of new extended\n  gamepad state.\n\n### Removed\n\n- `Gilrs::with_mappings()` – use `GilrsBuilder`.\n- `Gilrs::poll_events()` and `EventIterator` – use `Gilrs::next_event()`\n  instead.\n\n### Changed\n\n- Minimal rust version is now 1.19\n- New gamepad state. Now can store state for any button or axis (previously was\n  only useful for named buttons and axes). Additionally it now also know when\n  last event happened. Basic usage with `is_pressed()` and `value()` methods is\n  same, but check out documentation for new features.\n- Gamepad state now must be explicitly updated with `Gilrs::update(Event)`.\n  This change was necessary because filters can change events.\n- `Event` is now a struct and contains common information like id of gamepad\n  and timestamp (new). Old enum was renamed to `EventType` and can be accessed\n  from `Event.event` public field.\n- New force feedback module, including support for Windows. There are to many\n  changes to list them all here, so pleas check documentation and examples.\n- Renamed `ff::Error::EffectNotSupported` to `ff::Error::NotSupported`.\n- `Button::Unknown` and `Axis::Unknown` have now value of 0.\n- `Gamepad::set_mapping()` (and `_strict` variant) now returns error when\n  creating mapping with `Button::Unknown` or `Axis::Unknown`. Additionally\n  `_strict` version does not allow `Button::{C, Z}` and Axis::{LeftZ, RightZ}.\n- xinput: New values for `NativEvCode`\n\n### Fixed\n\n- Panic on `unreachable!()` when creating mapping with `Button::{C, Z,\n  Unknown}` or `Axis::{LeftZ, RightZ}`.\n\nv0.4.4 — 2017-06-16\n-------------------\n\n### Changed\n\n- Gilrs no longer uses `ioctl` crate on Linux. Because `ioctl` was deprecated\n  and all versions yanked, it was causing problems for new builds that didn't\n  have `ioctl` crate listed in Cargo.lock.\n\nv0.4.3 — 2017-03-12\n-------------------\n\n### Added\n\n- You can now iterate over mutable references to connected gamepads using\n  `Gilrs::gamepads_mut()`.\n\n### Fixed\n\n- Fixed `unreachable!()` panic on 32bit Linux\n- Improved converting axes values to `f32` when using XInput\n\nv0.4.2 - 2017-01-15\n-------------------\n\n### Changed\n\n- Updated SDL_GameControllerDB to latest revision.\n- Changes in axes values that are less than 1% are now ignored.\n\n### Fixed\n\n- Fixed multiple axes mapped to same axis name when mappings are incomplete.\n- Values returned with `AxisChanged` event now have correctly applied\n  deadzones.\n- Linux: Correctly handle event queue overrun.\n\nv0.4.1 - 2016-12-12\n-------------------\n\n### Fixed\n\n- Type inference error introduced by generic index in `<[T]>::get`\n\nv0.4.0 - 2016-12-11\n-------------------\n\n### Added\n\n- `Gamepad::mappings_source(&self)` which can be used to filter gamepads which\n  not provide unified controller layout\n- `MappingsSource` enum\n- You can now set custom mapping for gamepad with `Gamepad::set_mapping(…)`\n- `Gilrs::with_mappings(&str)` to create Gilrs with additional gamepad mappings\n\n### Changed\n\n- Button and axis events now also have native event codes\n- On Linux, if button or axis is not known, is now reported as `Unknown`\n  (previously all unknown events have been ignored)\n- More devices are now treated as gamepads on Linux (use `mappings_source()` to\n  filter unwanted gamepads)\n- Renamed `{Gamepad,GamepadState}::is_btn_pressed(Button)` to\n  `is_pressed(Button)`\n- Renamed `{Gamepad,GamepadState}::axis_val(Axis)` to `value(Axis)`\n\n### Fixed\n\n- Integer overflow if button with keyboard code was pressed on Linux\n- `Gilrs` should no longer panic if there are some unexpected problems with\n  Udev\n- Fixed normalization of axes values on Linux\n\nv0.3.1 - 2016-09-23\n-------------------\n\n### Fixed\n\n- Fixed compilation error on non-x86_64 Linux\n\nv0.3.0 - 2016-09-22\n-------------------\n\n### Added\n\n- `Gamepad::power_info(&self)`\n- `ff::Direction::from_radians(f32)` and `ff::Direction::from_vector([f32; 2])`\n- `Gilrs::gamepads(&self)` which returns iterator over all connected gamepads\n- `GamepadState` now implements `is_btn_pressed(Button)` and `axis_val(Axis)`\n- `Gilrs` now implements `Index`and `IndexMut`\n\n### Changed\n\n- Rename `Button::Unknow` to `Button::Unknown`\n- `Gamepad::name(&self)` now returns `&str` instead of `&String`\n- Improved dead zone detection\n- `Effect::play(&self, u16)` now returns `Result<(), Error>`\n- Linux: Reduced memory usage\n\n### Removed\n\n- `ff::Direction` no longer implements `From<f32>`\n\n### Fixed\n\n- Buttons west and east are no longer swapped when using SDL2 mappings\n- Linux: infinite loop after gamepad disconnects\n- Linux: SDL2 mappings for gamepads that can also report mouse and keyboard\n  events now should works\n\nv0.2.0 - 2016-08-18\n------\n\n### Changed\n\n- Rename `Gilrs::pool_events()` to `Gilrs::poll_events()`\n\n### Fixed\n\n- Linux: Disconnected events are now emitted properly\n- Linux: All force feedback effects are now dropped when gamepad disconnects\n"
  },
  {
    "path": "gilrs/Cargo.toml",
    "content": "[package]\nname = \"gilrs\"\nversion = \"0.11.0\"\nauthors = [\"Arvamer <arvamer@gmail.com>\"]\nlicense = \"Apache-2.0/MIT\"\nexclude = [\"controller.svg\"]\ndescription = \"Game Input Library for Rust\"\ndocumentation = \"https://docs.rs/gilrs/\"\nrepository = \"https://gitlab.com/gilrs-project/gilrs\"\nreadme = \"../README.md\"\nkeywords = [\"gamepad\", \"joystick\", \"input\"]\ncategories = [\"game-engines\"]\nedition = \"2021\"\nrust-version = \"1.80.0\"\n\n[badges]\ngitlab = { repository = \"gilrs-project/gilrs\" }\n\n[dependencies]\nvec_map = \"0.8\"\nuuid = \"1.0.0\"\nlog = \"0.4.1\"\nfnv = \"1.0\"\nserde = { version = \"1.0\", features = [\"derive\"], optional = true }\ngilrs-core = { path = \"../gilrs-core\", version = \"0.6.0\", default-features = false }\n\n[dev-dependencies]\neframe = \"0.30.0\"\nenv_logger = \"0.11.5\"\nconsole_log = \"1.0.0\"\negui_plot = \"0.30.0\"\nweb-sys = \"0.3.76\"\nwasm-bindgen-futures = \"0.4.49\"\n\n[target.'cfg(not(target_arch = \"wasm32\"))'.dev-dependencies]\n# fixed to avoid msrv bump to 1.81 -- can remove once we're past that\nhome = \"=0.5.9\"\n\n[target.'cfg(target_arch = \"wasm32\")'.dev-dependencies]\nconsole_error_panic_hook = \"0.1.7\"\n\n[package.metadata.docs.rs]\nfeatures = [\"serde-serialize\"]\n\n[features]\ndefault = [\"wgi\"]\nserde-serialize = [\"serde\", \"gilrs-core/serde-serialize\"]\nxinput = [\"gilrs-core/xinput\"]\nwgi = [\"gilrs-core/wgi\"]"
  },
  {
    "path": "gilrs/build.rs",
    "content": "//! This build script takes the gamecontrollerdb.txt from the SDL repo and removes any\n//! mappings that aren't for the current platform and removes comments etc.\n//!\n//! This reduces the binary size fairly significantly compared to including mappings for every\n//! platform.\n//! Especially Wasm since it doesn't use SDL mappings and binary size is important.\n\nuse std::env;\nuse std::fs::File;\nuse std::io::{BufRead, BufReader, Write};\nuse std::path::{Path, PathBuf};\n\n#[cfg(windows)]\nconst PATH_SEPARATOR: &str = \"backslash\";\n\n#[cfg(not(windows))]\nconst PATH_SEPARATOR: &str = \"slash\";\n\nfn main() {\n    println!(\"cargo:rustc-check-cfg=cfg(path_separator, values(\\\"slash\\\",\\\"backslash\\\"))\");\n    println!(r#\"cargo:rustc-cfg=path_separator=\"{}\"\"#, PATH_SEPARATOR);\n\n    let out_dir = env::var(\"OUT_DIR\").unwrap();\n    let cargo_manifest_dir = env::var(\"CARGO_MANIFEST_DIR\").unwrap();\n\n    let sdl_platform = \"platform:\".to_string()\n        + match env::var(\"CARGO_CFG_TARGET_FAMILY\").unwrap().as_str() {\n            \"unix\" => match env::var(\"CARGO_CFG_TARGET_OS\").unwrap().as_str() {\n                \"android\" => \"Android\",\n                \"macos\" => \"Mac OS X\",\n                _ => \"Linux\",\n            },\n            \"windows\" => \"Windows\",\n            \"wasm\" => \"Web\",\n            _ => \"Unknown\",\n        };\n\n    let sdl_game_controller_db_path: PathBuf =\n        PathBuf::from_iter(vec![\"SDL_GameControllerDB\", \"gamecontrollerdb.txt\"]);\n\n    // Tell cargo to re-run this script only when SDL's gamecontrollerdb.txt changes.\n    println!(\n        \"cargo:rerun-if-changed={}\",\n        sdl_game_controller_db_path.to_string_lossy()\n    );\n\n    let mut new_file = File::create(Path::new(&out_dir).join(\"gamecontrollerdb.txt\"))\n        .expect(\"failed to create gamecontrollerdb.txt for target\");\n\n    let path = Path::new(&cargo_manifest_dir).join(sdl_game_controller_db_path);\n\n    let original_file = File::open(&path).unwrap_or_else(|_| {\n        panic!(\n            \"Could not open gamecontrollerdb.txt {:?}. Did you forget to pull the \\\n             `SDL_GameControllerDB` submodule?\",\n            &path\n        )\n    });\n    let original_reader = BufReader::new(original_file);\n\n    original_reader\n        .lines()\n        .map(|x| match x {\n            Ok(x) => x,\n            Err(e) => panic!(\"Failed to read line from gamecontrollerdb.txt: {e}\"),\n        })\n        .filter(|line| {\n            line.trim_end()\n                .trim_end_matches(',')\n                .ends_with(&sdl_platform)\n        })\n        .for_each(|line| {\n            let mut line = line;\n            line.push('\\n');\n            new_file\n                .write_all(line.as_bytes())\n                .expect(\"Failed to write line to gamecontrollerdb.txt in OUT_DIR\");\n        });\n}\n"
  },
  {
    "path": "gilrs/examples/ev.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\n\nuse gilrs::ev::filter::{Filter, Repeat};\nuse gilrs::GilrsBuilder;\n\nuse std::process;\n\nfn main() {\n    env_logger::init();\n\n    let mut gilrs = match GilrsBuilder::new().set_update_state(false).build() {\n        Ok(g) => g,\n        Err(gilrs::Error::NotImplemented(g)) => {\n            eprintln!(\"Current platform is not supported\");\n\n            g\n        }\n        Err(e) => {\n            eprintln!(\"Failed to create gilrs context: {}\", e);\n            process::exit(-1);\n        }\n    };\n\n    let repeat_filter = Repeat::new();\n\n    loop {\n        while let Some(ev) = gilrs\n            .next_event_blocking(None)\n            .filter_ev(&repeat_filter, &mut gilrs)\n        {\n            gilrs.update(&ev);\n            println!(\"{:?}\", ev);\n        }\n\n        if gilrs.counter() % 25 == 0 {\n            for (id, gamepad) in gilrs.gamepads() {\n                println!(\n                    \"Power info of gamepad {}({}): {:?}\",\n                    id,\n                    gamepad.name(),\n                    gamepad.power_info()\n                );\n            }\n        }\n\n        gilrs.inc();\n    }\n}\n"
  },
  {
    "path": "gilrs/examples/ff.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\n\nuse gilrs::ff::{BaseEffect, BaseEffectType, EffectBuilder, Replay, Ticks};\nuse gilrs::Gilrs;\nuse std::thread;\nuse std::time::Duration;\n\nfn main() {\n    env_logger::init();\n    let mut gilrs = Gilrs::new().unwrap();\n    let support_ff = gilrs\n        .gamepads()\n        .filter_map(|(id, gp)| if gp.is_ff_supported() { Some(id) } else { None })\n        .collect::<Vec<_>>();\n\n    let duration = Ticks::from_ms(150);\n    let effect = EffectBuilder::new()\n        .add_effect(BaseEffect {\n            kind: BaseEffectType::Strong { magnitude: 60_000 },\n            scheduling: Replay {\n                play_for: duration,\n                with_delay: duration * 3,\n                ..Default::default()\n            },\n            envelope: Default::default(),\n        })\n        .add_effect(BaseEffect {\n            kind: BaseEffectType::Weak { magnitude: 60_000 },\n            scheduling: Replay {\n                after: duration * 2,\n                play_for: duration,\n                with_delay: duration * 3,\n            },\n            ..Default::default()\n        })\n        .gamepads(&support_ff)\n        .finish(&mut gilrs)\n        .unwrap();\n    effect.play().unwrap();\n\n    thread::sleep(Duration::from_secs(11));\n}\n"
  },
  {
    "path": "gilrs/examples/ff_pos.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\n\nuse gilrs::ff::{BaseEffect, BaseEffectType, DistanceModel, EffectBuilder};\nuse gilrs::{Axis, Button, EventType, Gilrs};\n\nuse std::io::{self, Write};\nuse std::thread;\nuse std::time::Duration;\n\n#[derive(Copy, Clone, PartialEq, Debug)]\nenum Modify {\n    DistModel,\n    RefDistance,\n    RolloffFactor,\n    MaxDistance,\n}\n\nimpl Modify {\n    fn next(&mut self) {\n        use crate::Modify::*;\n        *self = match *self {\n            DistModel => RefDistance,\n            RefDistance => RolloffFactor,\n            RolloffFactor => MaxDistance,\n            MaxDistance => DistModel,\n        };\n        print!(\"\\x1b[2K\\r{:?}\", self);\n        io::stdout().flush().unwrap();\n    }\n\n    fn prev(&mut self) {\n        use crate::Modify::*;\n        *self = match *self {\n            DistModel => MaxDistance,\n            RefDistance => DistModel,\n            RolloffFactor => RefDistance,\n            MaxDistance => RolloffFactor,\n        };\n        print!(\"\\x1b[2K\\r{:?}\", self);\n        io::stdout().flush().unwrap();\n    }\n}\n\nfn main() {\n    env_logger::init();\n    let mut gilrs = Gilrs::new().unwrap();\n\n    println!(\"Connected gamepads:\");\n\n    let mut support_ff = Vec::new();\n    for (idx, gp) in gilrs.gamepads() {\n        let ff = gp.is_ff_supported();\n        println!(\n            \"{}) {} ({})\",\n            idx,\n            gp.name(),\n            if ff {\n                \"Force feedback supported\"\n            } else {\n                \"Force feedback not supported\"\n            }\n        );\n        if ff {\n            support_ff.push(idx);\n        }\n    }\n\n    println!(\"----------------------------------------\");\n    println!(\n        \"Use sticks to move listener. Triggers change properties of distance model. South/west \\\n         button changes active property. Press east button on action pad to quit.\"\n    );\n\n    let pos1 = [-100.0, 0.0, 0.0];\n\n    let mut effect_builder = EffectBuilder::new()\n        .add_effect(BaseEffect {\n            kind: BaseEffectType::Strong { magnitude: 45_000 },\n            ..Default::default()\n        })\n        .add_effect(BaseEffect {\n            kind: BaseEffectType::Weak { magnitude: 45_000 },\n            ..Default::default()\n        })\n        .distance_model(DistanceModel::None)\n        .gamepads(&support_ff)\n        .clone();\n\n    let left_effect = effect_builder.position(pos1).finish(&mut gilrs).unwrap();\n\n    left_effect.play().unwrap();\n\n    println!(\"Playing one effects…\");\n    println!(\"Position of effect sources: {:?}\", pos1);\n\n    let mut listeners = support_ff\n        .iter()\n        .map(|&idx| (idx, [0.0, 0.0, 0.0]))\n        .collect::<Vec<_>>();\n\n    let mut ref_distance = 10.0;\n    let mut rolloff_factor = 0.5;\n    let mut max_distance = 100.0;\n    let mut modify = Modify::DistModel;\n    let mut model = 0usize;\n\n    'main: loop {\n        while let Some(event) = gilrs.next_event() {\n            match event.event {\n                EventType::ButtonReleased(Button::East, ..) => break 'main,\n                EventType::ButtonReleased(Button::South, ..) => modify.next(),\n                EventType::ButtonReleased(Button::West, ..) => modify.prev(),\n                EventType::ButtonReleased(Button::LeftTrigger, ..)\n                    if modify == Modify::DistModel =>\n                {\n                    model = model.wrapping_sub(1);\n                }\n                EventType::ButtonReleased(Button::RightTrigger, ..)\n                    if modify == Modify::DistModel =>\n                {\n                    model = model.wrapping_add(1);\n                }\n                _ => (),\n            }\n        }\n\n        for &mut (idx, ref mut pos) in &mut listeners {\n            let velocity = 0.5;\n\n            let gp = gilrs.gamepad(idx);\n            let (sx, sy) = (gp.value(Axis::LeftStickX), gp.value(Axis::LeftStickY));\n\n            if sx.abs() > 0.5 || sy.abs() > 0.5 {\n                if sx.abs() > 0.5 {\n                    pos[0] += velocity * sx.signum();\n                }\n                if sy.abs() > 0.5 {\n                    pos[1] += velocity * sy.signum();\n                }\n\n                gilrs.gamepad(idx).set_listener_position(*pos).unwrap();\n\n                let dist = ((pos[0] - pos1[0]).powi(2) + (pos[1] - pos1[1]).powi(2)).sqrt();\n                print!(\n                    \"\\x1b[2K\\rPosition of listener {:2} has changed: [{:6.1}, {:6.1}].Distance: \\\n                     {:.1}\",\n                    idx, pos[0], pos[1], dist\n                );\n                io::stdout().flush().unwrap();\n            }\n\n            let x = if gp.is_pressed(Button::LeftTrigger) {\n                -1.0\n            } else if gp.is_pressed(Button::RightTrigger) {\n                1.0\n            } else {\n                continue;\n            };\n\n            match modify {\n                Modify::RolloffFactor => rolloff_factor += x * velocity * 0.1,\n                Modify::RefDistance => ref_distance += x * velocity * 0.1,\n                Modify::MaxDistance => max_distance += x * velocity * 1.0,\n                Modify::DistModel => (), // DistanceModel handled in event loop\n            }\n\n            let model = match model % 4 {\n                0 => DistanceModel::None,\n                1 => DistanceModel::LinearClamped {\n                    ref_distance,\n                    rolloff_factor,\n                    max_distance,\n                },\n                2 => DistanceModel::InverseClamped {\n                    ref_distance,\n                    rolloff_factor,\n                    max_distance,\n                },\n                3 => DistanceModel::ExponentialClamped {\n                    ref_distance,\n                    rolloff_factor,\n                    max_distance,\n                },\n                _ => unreachable!(),\n            };\n\n            match left_effect.set_distance_model(model) {\n                Ok(()) => print!(\"\\x1b[2K\\r{:?}\", model),\n                Err(e) => print!(\"\\x1b[2K\\r{}\", e),\n            }\n            io::stdout().flush().unwrap();\n        }\n\n        thread::sleep(Duration::from_millis(16));\n    }\n}\n"
  },
  {
    "path": "gilrs/examples/gamepad_info.rs",
    "content": "use gilrs::{Axis, Button, Gilrs};\nuse uuid::Uuid;\n\nfn main() {\n    env_logger::init();\n    let gilrs = Gilrs::new().unwrap();\n    for (id, gamepad) in gilrs.gamepads() {\n        println!(\n            r#\"Gamepad {id} ({name}):\n    Map name: {map_name:?}\n    Os name: {os_name}\n    UUID: {uuid}\n    Is connected: {is_connected}\n    Power info: {power_info:?}\n    Mapping source: {mapping_source:?}\n    Is ff supported: {ff}\n    Deadzone Left X: {dlx:?}\n    Deadzone Left Y: {dly:?}\n    Deadzone Right X: {drx:?}\n    Deadzone Right Y: {dry:?}\n    Deadzone Left Trigger: {dlt:?}\n    Deadzone Right Trigger: {drt:?}\n    Deadzone Left Trigger 2: {dlt2:?}\n    Deadzone Right Trigger 2: {drt2:?}\n\"#,\n            id = id,\n            name = gamepad.name(),\n            map_name = gamepad.map_name(),\n            os_name = gamepad.os_name(),\n            uuid = Uuid::from_bytes(gamepad.uuid()).as_hyphenated(),\n            is_connected = gamepad.is_connected(),\n            power_info = gamepad.power_info(),\n            mapping_source = gamepad.mapping_source(),\n            ff = gamepad.is_ff_supported(),\n            dlx = gamepad\n                .axis_code(Axis::LeftStickX)\n                .and_then(|code| gamepad.deadzone(code)),\n            dly = gamepad\n                .axis_code(Axis::LeftStickY)\n                .and_then(|code| gamepad.deadzone(code)),\n            drx = gamepad\n                .axis_code(Axis::RightStickX)\n                .and_then(|code| gamepad.deadzone(code)),\n            dry = gamepad\n                .axis_code(Axis::RightStickY)\n                .and_then(|code| gamepad.deadzone(code)),\n            dlt = gamepad\n                .button_code(Button::LeftTrigger)\n                .and_then(|code| gamepad.deadzone(code)),\n            drt = gamepad\n                .button_code(Button::RightTrigger)\n                .and_then(|code| gamepad.deadzone(code)),\n            dlt2 = gamepad\n                .button_code(Button::LeftTrigger2)\n                .and_then(|code| gamepad.deadzone(code)),\n            drt2 = gamepad\n                .button_code(Button::RightTrigger2)\n                .and_then(|code| gamepad.deadzone(code)),\n        );\n    }\n}\n"
  },
  {
    "path": "gilrs/examples/gui.rs",
    "content": "#![cfg_attr(not(debug_assertions), windows_subsystem = \"windows\")] // hide console window on Windows in release\n\nuse eframe::egui;\nuse eframe::egui::Vec2;\nuse egui::RichText;\nuse egui_plot::{MarkerShape, Plot, PlotPoints, Points};\nuse gilrs::ev::AxisOrBtn;\nuse gilrs::ff::{BaseEffect, BaseEffectType, Effect, EffectBuilder, Repeat, Ticks};\nuse gilrs::{Axis, GamepadId, Gilrs, GilrsBuilder};\nuse gilrs_core::PowerInfo;\nuse std::time::UNIX_EPOCH;\nuse uuid::Uuid;\n\nstruct MyEguiApp {\n    gilrs: Gilrs,\n    current_gamepad: Option<GamepadId>,\n    log_messages: [Option<String>; 300],\n\n    // These will be none if Force feedback isn't supported for this platform e.g. Wasm\n    ff_strong: Option<Effect>,\n    ff_weak: Option<Effect>,\n}\n\nimpl Default for MyEguiApp {\n    fn default() -> Self {\n        #[cfg(target_arch = \"wasm32\")]\n        console_log::init().unwrap();\n        const INIT: Option<String> = None;\n        let mut gilrs = GilrsBuilder::new().set_update_state(false).build().unwrap();\n        let ff_strong = EffectBuilder::new()\n            .add_effect(BaseEffect {\n                kind: BaseEffectType::Strong { magnitude: 60_000 },\n                scheduling: Default::default(),\n                envelope: Default::default(),\n            })\n            .repeat(Repeat::For(Ticks::from_ms(100)))\n            .finish(&mut gilrs)\n            .ok();\n        let ff_weak = EffectBuilder::new()\n            .add_effect(BaseEffect {\n                kind: BaseEffectType::Weak { magnitude: 60_000 },\n                scheduling: Default::default(),\n                envelope: Default::default(),\n            })\n            .repeat(Repeat::For(Ticks::from_ms(100)))\n            .finish(&mut gilrs)\n            .ok();\n        Self {\n            gilrs,\n            current_gamepad: None,\n            log_messages: [INIT; 300],\n            ff_strong,\n            ff_weak,\n        }\n    }\n}\n\nimpl MyEguiApp {\n    fn log(&mut self, message: String) {\n        self.log_messages[0..].rotate_right(1);\n        self.log_messages[0] = Some(message);\n    }\n}\n\nimpl MyEguiApp {\n    fn new(_cc: &eframe::CreationContext<'_>) -> Self {\n        Self::default()\n    }\n}\n\nimpl eframe::App for MyEguiApp {\n    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {\n        while let Some(event) = self.gilrs.next_event() {\n            self.log(format!(\n                \"{} : {} : {:?}\",\n                event\n                    .time\n                    .duration_since(UNIX_EPOCH)\n                    .unwrap_or_default()\n                    .as_millis(),\n                event.id,\n                event.event\n            ));\n            self.gilrs.update(&event);\n            if self.current_gamepad.is_none() {\n                self.current_gamepad = Some(event.id);\n            }\n        }\n\n        egui::SidePanel::left(\"side_panel\").show(ctx, |ui| {\n            ui.heading(\"Controllers\");\n            ui.separator();\n\n            for (id, gamepad) in self.gilrs.gamepads() {\n                if ui\n                    .selectable_label(\n                        self.current_gamepad == Some(id),\n                        format!(\"{id}: {}\", gamepad.name()),\n                    )\n                    .clicked()\n                {\n                    self.current_gamepad = Some(id);\n                };\n            }\n            ui.allocate_space(ui.available_size());\n        });\n\n        egui::TopBottomPanel::bottom(\"log\")\n            .resizable(true)\n            .default_height(200.0)\n            .show(ctx, |ui| {\n                ui.heading(\"Event Log\");\n                egui::ScrollArea::vertical()\n                    .max_height(ui.available_height())\n                    .show(ui, |ui| {\n                        for message in self.log_messages.iter().flatten() {\n                            ui.label(message);\n                        }\n                        ui.allocate_space(ui.available_size());\n                    });\n            });\n\n        egui::CentralPanel::default().show(ctx, |ui| {\n            egui::ScrollArea::both().show(ui, |ui| {\n                if let Some(gamepad_id) = self.current_gamepad {\n                    let gamepad = self.gilrs.gamepad(gamepad_id);\n                    let gamepad_state = gamepad.state();\n                    ui.horizontal(|ui| {\n                        ui.vertical(|ui| {\n                            ui.heading(\"Info\");\n                            egui::Grid::new(\"info_grid\")\n                                .striped(true)\n                                .num_columns(2)\n                                .show(ui, |ui| {\n                                    ui.label(\"Name\");\n                                    ui.label(gamepad.name());\n                                    ui.end_row();\n\n                                    if let Some(vendor) = gamepad.vendor_id() {\n                                        ui.label(\"Vendor ID\");\n                                        ui.label(format!(\"{vendor:04x}\"));\n                                        ui.end_row();\n                                    }\n\n                                    if let Some(product) = gamepad.product_id() {\n                                        ui.label(\"Product ID\");\n                                        ui.label(format!(\"{product:04x}\"));\n                                        ui.end_row();\n                                    }\n\n                                    ui.label(\"Gilrs ID\");\n                                    ui.label(gamepad.id().to_string());\n                                    ui.end_row();\n\n                                    if let Some(map_name) = gamepad.map_name() {\n                                        ui.label(\"Map Name\");\n                                        ui.label(map_name);\n                                        ui.end_row();\n                                    }\n\n                                    ui.label(\"Map Source\");\n                                    ui.label(format!(\"{:?}\", gamepad.mapping_source()));\n                                    ui.end_row();\n\n                                    ui.label(\"Uuid\");\n                                    let uuid = Uuid::from_bytes(gamepad.uuid()).to_string();\n                                    ui.horizontal(|ui| {\n                                        ui.label(&uuid);\n                                        if ui.button(\"Copy\").clicked() {\n                                            ui.output_mut(|platform_output| {\n                                                platform_output.copied_text = uuid;\n                                            });\n                                        }\n                                    });\n                                    ui.end_row();\n\n                                    ui.label(\"Power\");\n                                    ui.label(match gamepad.power_info() {\n                                        PowerInfo::Unknown => \"Unknown\".to_string(),\n                                        PowerInfo::Wired => \"Wired\".to_string(),\n                                        PowerInfo::Discharging(p) => format!(\"Discharging {p}\"),\n                                        PowerInfo::Charging(p) => format!(\"Charging {p}\"),\n                                        PowerInfo::Charged => \"Charged\".to_string(),\n                                    });\n                                    ui.end_row();\n                                });\n                        });\n                        if gamepad.is_ff_supported() {\n                            ui.vertical(|ui| {\n                                ui.label(\"Force Feedback\");\n                                if let Some(ff_strong) = &self.ff_strong {\n                                    if ui.button(\"Play Strong\").clicked() {\n                                        ff_strong.add_gamepad(&gamepad).unwrap();\n                                        ff_strong.play().unwrap();\n                                    }\n                                }\n                                if let Some(ff_weak) = &self.ff_weak {\n                                    if ui.button(\"Play Weak\").clicked() {\n                                        ff_weak.add_gamepad(&gamepad).unwrap();\n                                        ff_weak.play().unwrap();\n                                    }\n                                }\n                            });\n                        }\n                    });\n                    ui.horizontal(|ui| {\n                        ui.vertical(|ui| {\n                            ui.set_width(300.0);\n                            ui.heading(\"Buttons\");\n\n                            for (code, button_data) in gamepad_state.buttons() {\n                                let name = match gamepad.axis_or_btn_name(code) {\n                                    Some(AxisOrBtn::Btn(b)) => format!(\"{b:?}\"),\n                                    _ => \"Unknown\".to_string(),\n                                };\n\n                                ui.add(\n                                    egui::widgets::ProgressBar::new(button_data.value()).text(\n                                        RichText::new(format!(\n                                            \"{name:<14} {:<5} {:.4} {}\",\n                                            button_data.is_pressed(),\n                                            button_data.value(),\n                                            code\n                                        ))\n                                        .monospace(),\n                                    ),\n                                );\n                            }\n                        });\n                        ui.vertical(|ui| {\n                            ui.set_width(300.0);\n                            ui.heading(\"Axes\");\n                            ui.horizontal(|ui| {\n                                for (name, x, y) in [\n                                    (\"Left Stick\", Axis::LeftStickX, Axis::LeftStickY),\n                                    (\"Right Stick\", Axis::RightStickX, Axis::RightStickY),\n                                ] {\n                                    ui.vertical(|ui| {\n                                        ui.label(name);\n                                        let y_axis = gamepad\n                                            .axis_data(y)\n                                            .map(|a| a.value())\n                                            .unwrap_or_default()\n                                            as f64;\n                                        let x_axis = gamepad\n                                            .axis_data(x)\n                                            .map(|a| a.value())\n                                            .unwrap_or_default()\n                                            as f64;\n                                        Plot::new(format!(\"{name}_plot\"))\n                                            .width(150.0)\n                                            .height(150.0)\n                                            .min_size(Vec2::splat(3.25))\n                                            .include_x(1.25)\n                                            .include_y(1.25)\n                                            .include_x(-1.25)\n                                            .include_y(-1.25)\n                                            .allow_drag(false)\n                                            .allow_zoom(false)\n                                            .allow_boxed_zoom(false)\n                                            .allow_scroll(false)\n                                            .show(ui, |plot_ui| {\n                                                plot_ui.points(\n                                                    Points::new(PlotPoints::new(vec![[\n                                                        x_axis, y_axis,\n                                                    ]]))\n                                                    .shape(MarkerShape::Circle)\n                                                    .radius(4.0),\n                                                );\n                                            });\n                                    });\n                                }\n                            });\n                            for (code, axis_data) in gamepad_state.axes() {\n                                let name = match gamepad.axis_or_btn_name(code) {\n                                    None => code.to_string(),\n                                    Some(AxisOrBtn::Btn(b)) => format!(\"{b:?}\"),\n                                    Some(AxisOrBtn::Axis(a)) => format!(\"{a:?}\"),\n                                };\n                                ui.add(\n                                    egui::widgets::ProgressBar::new(\n                                        (axis_data.value() * 0.5) + 0.5,\n                                    )\n                                    .text(\n                                        RichText::new(format!(\n                                            \"{:+.4} {name:<15} {}\",\n                                            axis_data.value(),\n                                            code\n                                        ))\n                                        .monospace(),\n                                    ),\n                                );\n                            }\n                        });\n                    });\n                } else {\n                    ui.label(\"Press a button on a controller or select it from the left.\");\n                }\n                ui.allocate_space(ui.available_size());\n            });\n        });\n\n        ctx.request_repaint();\n    }\n}\n\n#[cfg(not(target_arch = \"wasm32\"))]\nfn main() {\n    env_logger::init();\n    let native_options = eframe::NativeOptions {\n        viewport: egui::ViewportBuilder::default().with_inner_size(Vec2::new(1024.0, 768.0)),\n        ..Default::default()\n    };\n    let _ = eframe::run_native(\n        \"Gilrs Input Tester\",\n        native_options,\n        Box::new(|cc| Ok(Box::new(MyEguiApp::new(cc)))),\n    );\n}\n\n#[cfg(target_arch = \"wasm32\")]\nfn main() {\n    use eframe::wasm_bindgen::JsCast;\n\n    console_error_panic_hook::set_once();\n    let web_options = eframe::WebOptions::default();\n\n    wasm_bindgen_futures::spawn_local(async {\n        let document = web_sys::window()\n            .expect(\"No window\")\n            .document()\n            .expect(\"No document\");\n\n        let canvas = document\n            .get_element_by_id(\"the_canvas_id\")\n            .expect(\"Failed to find the_canvas_id\")\n            .dyn_into::<web_sys::HtmlCanvasElement>()\n            .expect(\"the_canvas_id was not a HtmlCanvasElement\");\n\n        let _ = eframe::WebRunner::new()\n            .start(\n                canvas,\n                web_options,\n                Box::new(|cc| Ok(Box::new(MyEguiApp::new(cc)))),\n            )\n            .await;\n    });\n}\n"
  },
  {
    "path": "gilrs/examples/wasm/.gitignore",
    "content": "target/\n"
  },
  {
    "path": "gilrs/examples/wasm/README.md",
    "content": "# Wasm Example\n\nThese are instructions for running the GUI example in your web browser using Wasm.\n\nCurrently only the GUI example is set up to run with Wasm.\n\n### Ubuntu requirements\n```bash\nsudo apt install build-essential\nsudo apt-get install libssl-dev pkg-config\n```\n\n### Setup\n\n```pwsh\nrustup target add wasm32-unknown-unknown\ncargo install wasm-bindgen-cli\ncargo install basic-http-server\n```\n\n\n### Build and Run\n\nRun these from the workspace root.\n\n```pwsh\ncargo build --release --example gui --target wasm32-unknown-unknown\nwasm-bindgen --out-name wasm_example --out-dir gilrs/examples/wasm/target --target web target/wasm32-unknown-unknown/release/examples/gui.wasm\nbasic-http-server gilrs/examples/wasm\n```\n\nNow open your web browser and navigate to http://127.0.0.1:4000\n"
  },
  {
    "path": "gilrs/examples/wasm/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n    <head>\n        <meta charset=\"UTF-8\"/>\n        <title>Gilrs Example</title>\n        <style>\n            html {\n                /* Remove touch delay: */\n                touch-action: manipulation;\n            }\n    \n            body {\n                /* Light mode background color for what is not covered by the egui canvas,\n                or where the egui canvas is translucent. */\n                background: #909090;\n            }\n    \n            @media (prefers-color-scheme: dark) {\n                body {\n                    /* Dark mode background color for what is not covered by the egui canvas,\n                    or where the egui canvas is translucent. */\n                    background: #404040;\n                }\n            }\n    \n            /* Allow canvas to fill entire web page: */\n            html,\n            body {\n                overflow: hidden;\n                margin: 0 !important;\n                padding: 0 !important;\n                height: 100%;\n                width: 100%;\n            }\n    \n            /* Make canvas fill entire document: */\n            canvas {\n                margin-right: auto;\n                margin-left: auto;\n                display: block;\n                position: absolute;\n                top: 0;\n                left: 0;\n                width: 100%;\n                height: 100%;\n            }\n    \n            .centered {\n                margin-right: auto;\n                margin-left: auto;\n                display: block;\n                position: absolute;\n                top: 50%;\n                left: 50%;\n                transform: translate(-50%, -50%);\n                color: #f0f0f0;\n                font-size: 24px;\n                font-family: Ubuntu-Light, Helvetica, sans-serif;\n                text-align: center;\n            }\n        </style>\n    </head>\n    <!-- The WASM code will resize the canvas dynamically -->\n    <!-- the id is hardcoded in main.rs . so, make sure both match. -->\n    <canvas id=\"the_canvas_id\"></canvas>\n    <script type=\"module\">\n        import init from './target/wasm_example.js'\n\n        init()\n    </script>\n    <canvas id=\"canvas\"></canvas>\n</html>\n"
  },
  {
    "path": "gilrs/examples/wasm/wasm_gui.ps1",
    "content": "### This script can be used instead of the \"Build and Run\" step in `./gilrs/examples/wasm/README.md`.\n### Useful for gilrs devs that want a single script to to point their IDE to for run configurations.\n### Supports Powershell 5 and up on Windows or Linux\n### Make sure to run the install steps from the readme first.\n\n# Start at this script's path and go up three levels to the workspace root.\n# Ensures a consistent path regardless of the working directory when you run the script.\n$Path = $PSScriptRoot | Split-Path | Split-Path | Split-Path\n$ProjectDir = Resolve-Path $Path\nSet-Location $ProjectDir\n\ncargo build --release --example gui --target wasm32-unknown-unknown\nwasm-bindgen --out-name wasm_example --out-dir gilrs/examples/wasm/target --target web target/wasm32-unknown-unknown/release/examples/gui.wasm\nbasic-http-server gilrs/examples/wasm\n"
  },
  {
    "path": "gilrs/examples/wasm/wasm_gui.sh",
    "content": "#!/usr/bin/env bash\n\n### This script can be used instead of the \"Build and Run\" step in `./gilrs/examples/wasm/README.md`.\n### Useful for gilrs devs that want a single script to to point their IDE to for run configurations.\n### Make sure to run the install steps from the readme first.\n\nset -e\n\n# Start at this script's path and go up three levels to the workspace root.\n# Ensures a consistent path regardless of the working directory when you run the script.\nSCRIPT_DIR=$( cd -- \"$( dirname -- \"${BASH_SOURCE[0]}\" )\" &> /dev/null && pwd )\nPROJECT_DIR=$(dirname \"$(dirname \"$(dirname \"$SCRIPT_DIR\")\")\")\ncd \"$PROJECT_DIR\" || exit\n\ncargo build --release --example gui --target wasm32-unknown-unknown\nwasm-bindgen --out-name wasm_example --out-dir gilrs/examples/wasm/target --target web target/wasm32-unknown-unknown/release/examples/gui.wasm\nbasic-http-server gilrs/examples/wasm\n"
  },
  {
    "path": "gilrs/src/constants.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\n\npub const BTN_UNKNOWN: u16 = 0;\n\npub const BTN_SOUTH: u16 = 1;\npub const BTN_EAST: u16 = 2;\npub const BTN_C: u16 = 3;\npub const BTN_NORTH: u16 = 4;\npub const BTN_WEST: u16 = 5;\npub const BTN_Z: u16 = 6;\npub const BTN_LT: u16 = 7;\npub const BTN_RT: u16 = 8;\npub const BTN_LT2: u16 = 9;\npub const BTN_RT2: u16 = 10;\npub const BTN_SELECT: u16 = 11;\npub const BTN_START: u16 = 12;\npub const BTN_MODE: u16 = 13;\npub const BTN_LTHUMB: u16 = 14;\npub const BTN_RTHUMB: u16 = 15;\n\npub const BTN_DPAD_UP: u16 = 16;\npub const BTN_DPAD_DOWN: u16 = 17;\npub const BTN_DPAD_LEFT: u16 = 18;\npub const BTN_DPAD_RIGHT: u16 = 19;\n\npub const AXIS_UNKNOWN: u16 = 0;\n\npub const AXIS_LSTICKX: u16 = 1;\npub const AXIS_LSTICKY: u16 = 2;\npub const AXIS_LEFTZ: u16 = 3;\npub const AXIS_RSTICKX: u16 = 4;\npub const AXIS_RSTICKY: u16 = 5;\npub const AXIS_RIGHTZ: u16 = 6;\npub const AXIS_DPADX: u16 = 7;\npub const AXIS_DPADY: u16 = 8;\n"
  },
  {
    "path": "gilrs/src/ev/filter.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\n\n//! Alter events in various ways.\n//!\n//! This modules contains \"event filters\" that can change, drop or create new events. To use them,\n//! import `Filter` trait and call `filter()` function on `Option<Event>`. Because `filter` also\n//! returns `Option<Event>` you can combine multiple filters by using `filter()` function on\n//! returned event.\n//!\n//! Filters in this modules have public fields that can be used to configure their behaviour. You\n//! can also create them with default values using `new()` method. If filter is not configurable,\n//! it is implemented as function (for example `deadzone()`).\n//!\n//! # Example\n//!\n//! ```\n//! use gilrs::{GilrsBuilder, Filter};\n//! use gilrs::ev::filter::{Jitter, Repeat, deadzone};\n//!\n//! let mut gilrs = GilrsBuilder::new().with_default_filters(false).build().unwrap();\n//! let jitter = Jitter { threshold: 0.02 };\n//! let repeat = Repeat::new();\n//!\n//! // Event loop\n//! loop {\n//!     while let Some(event) = gilrs\n//!         .next_event()\n//!         .filter_ev(&jitter, &mut gilrs)\n//!         .filter_ev(&deadzone, &mut gilrs)\n//!         .filter_ev(&repeat, &mut gilrs)\n//!     {\n//!         gilrs.update(&event);\n//!         println!(\"{:?}\", event);\n//!     }\n//!     # break;\n//! }\n//! ```\n//! # Implementing custom filters\n//!\n//! If you want to implement your own filters, you will have to implement `FilterFn` trait.\n//! **Do not return `None` if you got `Some(event)`**. If you want to discard an event, uses\n//! `EventType::Dropped`. Returning `None` means that there are no more events to process and\n//! will end `while let` loop.\n//!\n//! ## Example\n//!\n//! Example implementations of filter that will drop all events with `Unknown` axis or button.\n//!\n//! ```\n//! use gilrs::ev::filter::FilterFn;\n//! use gilrs::{Gilrs, Event, EventType, Button, Axis, Filter};\n//!\n//! struct UnknownSlayer;\n//!\n//! impl FilterFn for UnknownSlayer {\n//!     fn filter(&self, ev: Option<Event>, _gilrs: &mut Gilrs) -> Option<Event> {\n//!         match ev {\n//!             Some(Event { event: EventType::ButtonPressed(Button::Unknown, ..), id, .. })\n//!             | Some(Event { event: EventType::ButtonReleased(Button::Unknown, ..), id, .. })\n//!             | Some(Event { event: EventType::AxisChanged(Axis::Unknown, ..), id, .. })\n//!             => Some(Event::new(id, EventType::Dropped)),\n//!             _ => ev,\n//!         }\n//!     }\n//! }\n//! ```\n//!\n//! `FilterFn` is also implemented for all `Fn(Option<Event>, &Gilrs) -> Option<Event>`, so above\n//! example could be simplified to passing closure to `filter()` function.\n\nuse crate::ev::{Axis, AxisOrBtn, Button, Code, Event, EventType};\nuse crate::gamepad::{Gamepad, Gilrs};\nuse crate::utils;\n\nuse std::time::Duration;\n\n/// Discard axis events that changed less than `threshold`.\n#[derive(Copy, Clone, PartialEq, Debug)]\npub struct Jitter {\n    pub threshold: f32,\n}\n\nimpl Jitter {\n    /// Creates new `Jitter` filter with threshold set to 0.01.\n    pub fn new() -> Self {\n        Jitter { threshold: 0.01 }\n    }\n}\n\nimpl Default for Jitter {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl FilterFn for Jitter {\n    fn filter(&self, ev: Option<Event>, gilrs: &mut Gilrs) -> Option<Event> {\n        match ev {\n            Some(Event {\n                event: EventType::AxisChanged(_, val, axis),\n                id,\n                ..\n            }) => match gilrs.gamepad(id).state().axis_data(axis) {\n                Some(data) if val != 0.0 && (val - data.value()).abs() < self.threshold => {\n                    Some(Event::new(id, EventType::Dropped))\n                }\n                _ => ev,\n            },\n            _ => ev,\n        }\n    }\n}\n\nfn apply_deadzone(x: f32, y: f32, threshold: f32) -> (f32, f32) {\n    let magnitude = utils::clamp((x * x + y * y).sqrt(), 0.0, 1.0);\n    if magnitude <= threshold {\n        (0.0, 0.0)\n    } else {\n        let norm = ((magnitude - threshold) / (1.0 - threshold)) / magnitude;\n        (x * norm, y * norm)\n    }\n}\n\nfn deadzone_nonzero_axis_idx(axis: Axis) -> Option<usize> {\n    Some(match axis {\n        Axis::DPadX => 0,\n        Axis::DPadY => 1,\n        Axis::LeftStickX => 2,\n        Axis::LeftStickY => 3,\n        Axis::RightStickX => 4,\n        Axis::RightStickY => 5,\n        _ => {\n            return None;\n        }\n    })\n}\n\n/// Drops events in dead zone and remaps value to keep it in standard range.\npub fn deadzone(ev: Option<Event>, gilrs: &mut Gilrs) -> Option<Event> {\n    match ev {\n        Some(Event {\n            event: EventType::AxisChanged(axis, val, nec),\n            id,\n            time,\n        }) => {\n            let threshold = match gilrs.gamepad(id).deadzone(nec) {\n                Some(t) => t,\n                None => return ev,\n            };\n\n            if let Some((other_axis, other_code)) = axis\n                .second_axis()\n                .and_then(|axis| gilrs.gamepad(id).axis_code(axis).map(|code| (axis, code)))\n            {\n                let other_val = gilrs.gamepad(id).state().value(other_code);\n                let val = apply_deadzone(val, other_val, threshold);\n\n                // Since this is the second axis, deadzone_nonzero_axis_idx() will always returns something.\n                let other_axis_idx = deadzone_nonzero_axis_idx(other_axis).unwrap();\n\n                if val.0 == 0.\n                    && val.1 == 0.\n                    && gilrs.gamepads_data[id.0].have_sent_nonzero_for_axis[other_axis_idx]\n                    && gilrs.gamepad(id).state().value(other_code) != 0.\n                {\n                    // Clear other axis that is now within the dead zone threshold.\n                    gilrs.insert_event(Event {\n                        id,\n                        time,\n                        event: EventType::AxisChanged(other_axis, 0., other_code),\n                    });\n                    gilrs.gamepads_data[id.0].have_sent_nonzero_for_axis[other_axis_idx] = false;\n                }\n\n                Some(if gilrs.gamepad(id).state().value(nec) == val.0 {\n                    Event::new(id, EventType::Dropped)\n                } else {\n                    if let Some(axis_idx) = deadzone_nonzero_axis_idx(axis) {\n                        gilrs.gamepads_data[id.0].have_sent_nonzero_for_axis[axis_idx] =\n                            val.0 != 0.;\n                    }\n                    Event {\n                        id,\n                        time,\n                        event: EventType::AxisChanged(axis, val.0, nec),\n                    }\n                })\n            } else {\n                let val = apply_deadzone(val, 0.0, threshold).0;\n\n                Some(if gilrs.gamepad(id).state().value(nec) == val {\n                    Event::new(id, EventType::Dropped)\n                } else {\n                    if let Some(axis_idx) = deadzone_nonzero_axis_idx(axis) {\n                        gilrs.gamepads_data[id.0].have_sent_nonzero_for_axis[axis_idx] = val != 0.;\n                    }\n                    Event {\n                        id,\n                        time,\n                        event: EventType::AxisChanged(axis, val, nec),\n                    }\n                })\n            }\n        }\n        Some(Event {\n            event: EventType::ButtonChanged(btn, val, nec),\n            id,\n            time,\n        }) => {\n            let gp = &gilrs.gamepad(id);\n            let threshold = match gp.deadzone(nec) {\n                Some(t) => t,\n                None => return ev,\n            };\n            let val = apply_deadzone(val, 0.0, threshold).0;\n\n            Some(if gp.state().value(nec) == val {\n                Event::new(id, EventType::Dropped)\n            } else {\n                Event {\n                    id,\n                    time,\n                    event: EventType::ButtonChanged(btn, val, nec),\n                }\n            })\n        }\n        _ => ev,\n    }\n}\n\n/// Maps axis dpad events to button dpad events.\n///\n/// This filter will do nothing if gamepad has dpad buttons (to prevent double events for same\n/// element) and if standard `NativeEvCode` for dpads is used by some other buttons. It will always\n/// try to map if SDL mappings contains mappings for all four hats.\npub fn axis_dpad_to_button(ev: Option<Event>, gilrs: &mut Gilrs) -> Option<Event> {\n    use gilrs_core::native_ev_codes as necs;\n\n    fn can_map(gp: &Gamepad<'_>) -> bool {\n        let hats_mapped = gp.mapping().hats_mapped();\n        if hats_mapped == 0b0000_1111 {\n            true\n        } else if hats_mapped == 0 {\n            gp.axis_or_btn_name(Code(necs::BTN_DPAD_RIGHT)).is_none()\n                && gp.axis_or_btn_name(Code(necs::BTN_DPAD_LEFT)).is_none()\n                && gp.axis_or_btn_name(Code(necs::BTN_DPAD_DOWN)).is_none()\n                && gp.axis_or_btn_name(Code(necs::BTN_DPAD_UP)).is_none()\n                && gp.button_code(Button::DPadRight).is_none()\n        } else {\n            // Not all hats are mapped so let's ignore it for now.\n            false\n        }\n    }\n\n    let ev = ev?;\n    let gamepad = gilrs.gamepad(ev.id);\n\n    if !can_map(&gamepad) {\n        return Some(ev);\n    }\n\n    let mut out_event = ev.drop();\n\n    match ev.event {\n        EventType::AxisChanged(Axis::DPadX, val, _) => {\n            let mut release_left = false;\n            let mut release_right = false;\n\n            if val == 1.0 {\n                // The axis value might change from left (-1.0) to right (1.0) immediately without\n                // us getting an additional event for the release at the center position (0.0).\n                release_left = gamepad.state().is_pressed(Code(necs::BTN_DPAD_LEFT));\n\n                gilrs.insert_event(Event {\n                    event: EventType::ButtonChanged(\n                        Button::DPadRight,\n                        1.0,\n                        Code(necs::BTN_DPAD_RIGHT),\n                    ),\n                    ..ev\n                });\n                out_event = Event {\n                    event: EventType::ButtonPressed(Button::DPadRight, Code(necs::BTN_DPAD_RIGHT)),\n                    ..ev\n                };\n            } else if val == -1.0 {\n                // The axis value might change from right (1.0) to left (-1.0) immediately without\n                // us getting an additional event for the release at the center position (0.0).\n                release_right = gamepad.state().is_pressed(Code(necs::BTN_DPAD_RIGHT));\n\n                gilrs.insert_event(Event {\n                    event: EventType::ButtonChanged(\n                        Button::DPadLeft,\n                        1.0,\n                        Code(necs::BTN_DPAD_LEFT),\n                    ),\n                    ..ev\n                });\n                out_event = Event {\n                    event: EventType::ButtonPressed(Button::DPadLeft, Code(necs::BTN_DPAD_LEFT)),\n                    ..ev\n                };\n            } else {\n                release_left = gamepad.state().is_pressed(Code(necs::BTN_DPAD_LEFT));\n                release_right = gamepad.state().is_pressed(Code(necs::BTN_DPAD_RIGHT));\n            }\n\n            if release_right {\n                if !out_event.is_dropped() {\n                    gilrs.insert_event(out_event);\n                }\n\n                gilrs.insert_event(Event {\n                    event: EventType::ButtonChanged(\n                        Button::DPadRight,\n                        0.0,\n                        Code(necs::BTN_DPAD_RIGHT),\n                    ),\n                    ..ev\n                });\n                out_event = Event {\n                    event: EventType::ButtonReleased(Button::DPadRight, Code(necs::BTN_DPAD_RIGHT)),\n                    ..ev\n                };\n            }\n\n            if release_left {\n                if !out_event.is_dropped() {\n                    gilrs.insert_event(out_event);\n                }\n\n                gilrs.insert_event(Event {\n                    event: EventType::ButtonChanged(\n                        Button::DPadLeft,\n                        0.0,\n                        Code(necs::BTN_DPAD_LEFT),\n                    ),\n                    ..ev\n                });\n                out_event = Event {\n                    event: EventType::ButtonReleased(Button::DPadLeft, Code(necs::BTN_DPAD_LEFT)),\n                    ..ev\n                };\n            }\n\n            Some(out_event)\n        }\n        EventType::AxisChanged(Axis::DPadY, val, _) => {\n            let mut release_up = false;\n            let mut release_down = false;\n\n            if val == 1.0 {\n                // The axis value might change from down (-1.0) to up (1.0) immediately without us\n                // getting an additional event for the release at the center position (0.0).\n                release_down = gamepad.state().is_pressed(Code(necs::BTN_DPAD_DOWN));\n\n                gilrs.insert_event(Event {\n                    event: EventType::ButtonChanged(Button::DPadUp, 1.0, Code(necs::BTN_DPAD_UP)),\n                    ..ev\n                });\n                out_event = Event {\n                    event: EventType::ButtonPressed(Button::DPadUp, Code(necs::BTN_DPAD_UP)),\n                    ..ev\n                };\n            } else if val == -1.0 {\n                // The axis value might change from up (1.0) to down (-1.0) immediately without us\n                // getting an additional event for the release at the center position (0.0).\n                release_up = gamepad.state().is_pressed(Code(necs::BTN_DPAD_UP));\n\n                gilrs.insert_event(Event {\n                    event: EventType::ButtonChanged(\n                        Button::DPadDown,\n                        1.0,\n                        Code(necs::BTN_DPAD_DOWN),\n                    ),\n                    ..ev\n                });\n                out_event = Event {\n                    event: EventType::ButtonPressed(Button::DPadDown, Code(necs::BTN_DPAD_DOWN)),\n                    ..ev\n                };\n            } else {\n                release_up = gamepad.state().is_pressed(Code(necs::BTN_DPAD_UP));\n                release_down = gamepad.state().is_pressed(Code(necs::BTN_DPAD_DOWN));\n            }\n\n            if release_up {\n                if !out_event.is_dropped() {\n                    gilrs.insert_event(out_event);\n                }\n\n                gilrs.insert_event(Event {\n                    event: EventType::ButtonChanged(Button::DPadUp, 0.0, Code(necs::BTN_DPAD_UP)),\n                    ..ev\n                });\n                out_event = Event {\n                    event: EventType::ButtonReleased(Button::DPadUp, Code(necs::BTN_DPAD_UP)),\n                    ..ev\n                };\n            }\n\n            if release_down {\n                if !out_event.is_dropped() {\n                    gilrs.insert_event(out_event);\n                }\n\n                gilrs.insert_event(Event {\n                    event: EventType::ButtonChanged(\n                        Button::DPadDown,\n                        0.0,\n                        Code(necs::BTN_DPAD_DOWN),\n                    ),\n                    ..ev\n                });\n                out_event = Event {\n                    event: EventType::ButtonReleased(Button::DPadDown, Code(necs::BTN_DPAD_DOWN)),\n                    ..ev\n                };\n            }\n\n            Some(out_event)\n        }\n        _ => Some(ev),\n    }\n}\n\n/// Repeats pressed keys.\n#[derive(Copy, Clone, PartialEq, Eq, Debug)]\npub struct Repeat {\n    pub after: Duration,\n    pub every: Duration,\n}\n\nimpl Repeat {\n    /// Creates new `Repeat` filter with `after` set to 500ms and `every` set to 30ms.\n    pub fn new() -> Self {\n        Repeat {\n            after: Duration::from_millis(500),\n            every: Duration::from_millis(30),\n        }\n    }\n}\n\nimpl Default for Repeat {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl FilterFn for Repeat {\n    fn filter(&self, ev: Option<Event>, gilrs: &mut Gilrs) -> Option<Event> {\n        match ev {\n            Some(ev) => Some(ev),\n            None => {\n                let now = utils::time_now();\n                for (id, gamepad) in gilrs.gamepads() {\n                    for (nec, btn_data) in gamepad.state().buttons() {\n                        match (\n                            btn_data.is_pressed(),\n                            btn_data.is_repeating(),\n                            now.duration_since(btn_data.timestamp()),\n                        ) {\n                            (true, false, Ok(dur)) if dur >= self.after => {\n                                let btn_name = match gamepad.axis_or_btn_name(nec) {\n                                    Some(AxisOrBtn::Btn(b)) => b,\n                                    _ => Button::Unknown,\n                                };\n\n                                return Some(Event {\n                                    id,\n                                    event: EventType::ButtonRepeated(btn_name, nec),\n                                    time: btn_data.timestamp() + self.after,\n                                });\n                            }\n                            (true, true, Ok(dur)) if dur >= self.every => {\n                                let btn_name = match gamepad.axis_or_btn_name(nec) {\n                                    Some(AxisOrBtn::Btn(b)) => b,\n                                    _ => Button::Unknown,\n                                };\n\n                                return Some(Event {\n                                    id,\n                                    event: EventType::ButtonRepeated(btn_name, nec),\n                                    time: btn_data.timestamp() + self.every,\n                                });\n                            }\n                            _ => (),\n                        }\n                    }\n                }\n                None\n            }\n        }\n    }\n}\n\n/// Allow filtering events.\n///\n/// See module level documentation for more info.\npub trait Filter {\n    fn filter_ev<F: FilterFn>(&self, filter: &F, gilrs: &mut Gilrs) -> Option<Event>;\n}\n\n/// Actual filter implementation.\n///\n/// See module level documentation for more info.\npub trait FilterFn {\n    fn filter(&self, ev: Option<Event>, gilrs: &mut Gilrs) -> Option<Event>;\n}\n\nimpl<F> FilterFn for F\nwhere\n    F: Fn(Option<Event>, &mut Gilrs) -> Option<Event>,\n{\n    fn filter(&self, ev: Option<Event>, gilrs: &mut Gilrs) -> Option<Event> {\n        self(ev, gilrs)\n    }\n}\n\nimpl Filter for Option<Event> {\n    fn filter_ev<F: FilterFn>(&self, filter: &F, gilrs: &mut Gilrs) -> Option<Event> {\n        let e = filter.filter(*self, gilrs);\n        debug_assert!(\n            !(self.is_some() && e.is_none()),\n            \"Filter changed Some(event) into None. See ev::filter documentation for more info.\"\n        );\n\n        e\n    }\n}\n\nimpl Filter for Event {\n    fn filter_ev<F: FilterFn>(&self, filter: &F, gilrs: &mut Gilrs) -> Option<Event> {\n        let e = filter.filter(Some(*self), gilrs);\n        debug_assert!(\n            e.is_some(),\n            \"Filter changed Some(event) into None. See ev::filter documentation for more info.\"\n        );\n\n        e\n    }\n}\n"
  },
  {
    "path": "gilrs/src/ev/mod.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\n\n//! Gamepad state and other event related functionality.\n\npub mod filter;\npub mod state;\n\nuse std::{\n    fmt::{Display, Formatter, Result as FmtResult},\n    time::SystemTime,\n};\n\nuse crate::{constants::*, gamepad::GamepadId, utils};\n\n#[cfg(feature = \"serde-serialize\")]\nuse serde::{Deserialize, Serialize};\n\n/// Platform specific event code.\n///\n/// This type represents single gamepads's element like specific axis or button.\n/// It can't be directly created, but you can get it from events or using\n/// `Gamepad`'s methods [`button_code`](crate::Gamepad::button_code) and\n/// [`axis_code`](crate::Gamepad::axis_code). If `serde-serialize` feature is\n/// enabled, `Code` can be serialized and deserialized, but keep in mind that\n/// layout **is** platform-specific. So it's not possible to serialize `Code` on\n/// Linux and deserialize it on Windows. This also apply to `Display` implementation.\n#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]\n#[cfg_attr(feature = \"serde-serialize\", derive(Serialize, Deserialize))]\npub struct Code(pub(crate) gilrs_core::EvCode);\n\nimpl Display for Code {\n    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {\n        self.0.fmt(f)\n    }\n}\n\nimpl Code {\n    pub fn into_u32(&self) -> u32 {\n        self.0.into_u32()\n    }\n}\n\n/// Holds information about gamepad event.\n#[derive(Copy, Clone, PartialEq, Debug)]\n#[cfg_attr(feature = \"serde-serialize\", derive(Serialize, Deserialize))]\n#[non_exhaustive]\npub struct Event {\n    /// Id of gamepad.\n    pub id: GamepadId,\n    /// Event's data.\n    pub event: EventType,\n    /// Time when event was emitted.\n    pub time: SystemTime,\n}\n\nimpl Event {\n    /// Creates new event with current time.\n    pub fn new(id: GamepadId, event: EventType) -> Self {\n        Event {\n            id,\n            event,\n            time: utils::time_now(),\n        }\n    }\n\n    /// Returns `Event` with `EventType::Dropped`.\n    pub fn drop(mut self) -> Event {\n        self.event = EventType::Dropped;\n\n        self\n    }\n\n    /// Returns true if event is `Dropped` and should be ignored.\n    pub fn is_dropped(&self) -> bool {\n        self.event == EventType::Dropped\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq)]\n#[cfg_attr(feature = \"serde-serialize\", derive(Serialize, Deserialize))]\n#[non_exhaustive]\n/// Gamepad event.\npub enum EventType {\n    /// Some button on gamepad has been pressed.\n    ButtonPressed(Button, Code),\n    /// This event can be generated by [`ev::Repeat`](filter/struct.Repeat.html) event filter.\n    ButtonRepeated(Button, Code),\n    /// Previously pressed button has been released.\n    ButtonReleased(Button, Code),\n    /// Value of button has changed. Value can be in range [0.0, 1.0].\n    ButtonChanged(Button, f32, Code),\n    /// Value of axis has changed. Value can be in range [-1.0, 1.0].\n    AxisChanged(Axis, f32, Code),\n    /// Gamepad has been connected. If gamepad's UUID doesn't match one of disconnected gamepads,\n    /// newly connected gamepad will get new ID.\n    Connected,\n    /// Gamepad has been disconnected. Disconnected gamepad will not generate any new events.\n    Disconnected,\n    /// There was an `Event`, but it was dropped by one of filters. You should ignore it.\n    Dropped,\n    /// A force feedback effect has ran for its duration and stopped.\n    ForceFeedbackEffectCompleted,\n}\n\n#[repr(u16)]\n#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)]\n#[cfg_attr(feature = \"serde-serialize\", derive(Serialize, Deserialize))]\n/// Gamepad's elements which state can be represented by value from 0.0 to 1.0.\n///\n/// ![Controller layout](https://gilrs-project.gitlab.io/gilrs/img/controller.svg)\npub enum Button {\n    // Action Pad\n    South = BTN_SOUTH,\n    East = BTN_EAST,\n    North = BTN_NORTH,\n    West = BTN_WEST,\n    C = BTN_C,\n    Z = BTN_Z,\n    // Triggers\n    LeftTrigger = BTN_LT,\n    LeftTrigger2 = BTN_LT2,\n    RightTrigger = BTN_RT,\n    RightTrigger2 = BTN_RT2,\n    // Menu Pad\n    Select = BTN_SELECT,\n    Start = BTN_START,\n    Mode = BTN_MODE,\n    // Sticks\n    LeftThumb = BTN_LTHUMB,\n    RightThumb = BTN_RTHUMB,\n    // D-Pad\n    DPadUp = BTN_DPAD_UP,\n    DPadDown = BTN_DPAD_DOWN,\n    DPadLeft = BTN_DPAD_LEFT,\n    DPadRight = BTN_DPAD_RIGHT,\n\n    #[default]\n    Unknown = BTN_UNKNOWN,\n}\n\nimpl Button {\n    pub fn is_action(self) -> bool {\n        use crate::Button::*;\n        matches!(self, South | East | North | West | C | Z)\n    }\n\n    pub fn is_trigger(self) -> bool {\n        use crate::Button::*;\n        matches!(\n            self,\n            LeftTrigger | LeftTrigger2 | RightTrigger | RightTrigger2\n        )\n    }\n\n    pub fn is_menu(self) -> bool {\n        use crate::Button::*;\n        matches!(self, Select | Start | Mode)\n    }\n\n    pub fn is_stick(self) -> bool {\n        use crate::Button::*;\n        matches!(self, LeftThumb | RightThumb)\n    }\n\n    pub fn is_dpad(self) -> bool {\n        use crate::Button::*;\n        matches!(self, DPadUp | DPadDown | DPadLeft | DPadRight)\n    }\n\n    pub fn to_nec(self) -> Option<Code> {\n        use gilrs_core::native_ev_codes as necs;\n\n        match self {\n            Button::South => Some(necs::BTN_SOUTH),\n            Button::East => Some(necs::BTN_EAST),\n            Button::North => Some(necs::BTN_NORTH),\n            Button::West => Some(necs::BTN_WEST),\n            Button::C => Some(necs::BTN_C),\n            Button::Z => Some(necs::BTN_Z),\n            Button::LeftTrigger => Some(necs::BTN_LT),\n            Button::LeftTrigger2 => Some(necs::BTN_LT2),\n            Button::RightTrigger => Some(necs::BTN_RT),\n            Button::RightTrigger2 => Some(necs::BTN_RT2),\n            Button::Select => Some(necs::BTN_SELECT),\n            Button::Start => Some(necs::BTN_START),\n            Button::Mode => Some(necs::BTN_MODE),\n            Button::LeftThumb => Some(necs::BTN_LTHUMB),\n            Button::RightThumb => Some(necs::BTN_RTHUMB),\n            Button::DPadUp => Some(necs::BTN_DPAD_UP),\n            Button::DPadDown => Some(necs::BTN_DPAD_DOWN),\n            Button::DPadLeft => Some(necs::BTN_DPAD_LEFT),\n            Button::DPadRight => Some(necs::BTN_DPAD_RIGHT),\n            _ => None,\n        }\n        .map(Code)\n    }\n}\n\n#[repr(u16)]\n#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]\n#[cfg_attr(feature = \"serde-serialize\", derive(Serialize, Deserialize))]\n/// Gamepad's elements which state can be represented by value from -1.0 to 1.0.\n///\n/// ![Controller layout](https://gilrs-project.gitlab.io/gilrs/img/controller.svg)\npub enum Axis {\n    LeftStickX = AXIS_LSTICKX,\n    LeftStickY = AXIS_LSTICKY,\n    LeftZ = AXIS_LEFTZ,\n    RightStickX = AXIS_RSTICKX,\n    RightStickY = AXIS_RSTICKY,\n    RightZ = AXIS_RIGHTZ,\n    DPadX = AXIS_DPADX,\n    DPadY = AXIS_DPADY,\n    Unknown = AXIS_UNKNOWN,\n}\n\nimpl Axis {\n    /// Returns true if axis is `LeftStickX`, `LeftStickY`, `RightStickX` or `RightStickY`.\n    pub fn is_stick(self) -> bool {\n        use crate::Axis::*;\n        matches!(self, LeftStickX | LeftStickY | RightStickX | RightStickY)\n    }\n\n    /// Returns the other axis from same element of gamepad, if any.\n    ///\n    /// | input       | output            |\n    /// |-------------|-------------------|\n    /// |`LeftStickX` |`Some(LeftStickY)` |\n    /// |`LeftStickY` |`Some(LeftStickX)` |\n    /// |`RightStickX`|`Some(RightStickY)`|\n    /// |`RightStickY`|`Some(RightStickX)`|\n    /// |`DpadX`      |`Some(DpadY)`      |\n    /// |`DpadY`      |`Some(DpadX)`      |\n    /// | …           |`None`             |\n    pub fn second_axis(self) -> Option<Self> {\n        use crate::Axis::*;\n        match self {\n            LeftStickX => Some(LeftStickY),\n            LeftStickY => Some(LeftStickX),\n            RightStickX => Some(RightStickY),\n            RightStickY => Some(RightStickX),\n            DPadX => Some(DPadY),\n            DPadY => Some(DPadX),\n            _ => None,\n        }\n    }\n}\n\n/// Represents `Axis` or `Button`.\n#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]\n#[cfg_attr(feature = \"serde-serialize\", derive(Serialize, Deserialize))]\npub enum AxisOrBtn {\n    Axis(Axis),\n    Btn(Button),\n}\n\nimpl AxisOrBtn {\n    pub(crate) fn is_button(&self) -> bool {\n        matches!(self, AxisOrBtn::Btn(_))\n    }\n}\n"
  },
  {
    "path": "gilrs/src/ev/state.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\n\nuse crate::ev::Code;\n\nuse fnv::FnvHashMap;\n\nuse std::collections::hash_map;\nuse std::iter::Iterator;\nuse std::time::SystemTime;\n\n/// Cached gamepad state.\n#[derive(Clone, Debug)]\npub struct GamepadState {\n    // Indexed by EvCode (nec)\n    buttons: FnvHashMap<Code, ButtonData>,\n    // Indexed by EvCode (nec)\n    axes: FnvHashMap<Code, AxisData>,\n}\n\nimpl GamepadState {\n    pub(crate) fn new() -> Self {\n        GamepadState {\n            buttons: FnvHashMap::default(),\n            axes: FnvHashMap::default(),\n        }\n    }\n\n    /// Returns `true` if given button is pressed. Returns `false` if there is no information about\n    /// `btn` or it is not pressed.\n    pub fn is_pressed(&self, btn: Code) -> bool {\n        self.buttons\n            .get(&btn)\n            .map(|s| s.is_pressed())\n            .unwrap_or(false)\n    }\n\n    /// Returns value of `el` or 0.0 when there is no information about it. `el` can be either axis\n    /// or button.\n    pub fn value(&self, el: Code) -> f32 {\n        self.axes\n            .get(&el)\n            .map(|s| s.value())\n            .or_else(|| self.buttons.get(&el).map(|s| s.value()))\n            .unwrap_or(0.0)\n    }\n\n    /// Iterate over buttons data.\n    pub fn buttons(&self) -> ButtonDataIter<'_> {\n        ButtonDataIter(self.buttons.iter())\n    }\n\n    /// Iterate over axes data.\n    pub fn axes(&self) -> AxisDataIter<'_> {\n        AxisDataIter(self.axes.iter())\n    }\n\n    /// Returns button state and when it changed.\n    pub fn button_data(&self, btn: Code) -> Option<&ButtonData> {\n        self.buttons.get(&btn)\n    }\n\n    /// Returns axis state and when it changed.\n    pub fn axis_data(&self, axis: Code) -> Option<&AxisData> {\n        self.axes.get(&axis)\n    }\n\n    pub(crate) fn set_btn_pressed(\n        &mut self,\n        btn: Code,\n        pressed: bool,\n        counter: u64,\n        timestamp: SystemTime,\n    ) {\n        let data = self.buttons.entry(btn).or_insert_with(|| {\n            ButtonData::new(\n                if pressed { 1.0 } else { 0.0 },\n                pressed,\n                false,\n                counter,\n                timestamp,\n            )\n        });\n        data.is_pressed = pressed;\n        data.is_repeating = false;\n        data.counter = counter;\n        data.last_event_ts = timestamp;\n    }\n\n    pub(crate) fn set_btn_repeating(&mut self, btn: Code, counter: u64, timestamp: SystemTime) {\n        let data = self\n            .buttons\n            .entry(btn)\n            .or_insert_with(|| ButtonData::new(1.0, true, true, counter, timestamp));\n        data.is_repeating = true;\n        data.counter = counter;\n        data.last_event_ts = timestamp;\n    }\n\n    pub(crate) fn set_btn_value(\n        &mut self,\n        btn: Code,\n        value: f32,\n        counter: u64,\n        timestamp: SystemTime,\n    ) {\n        let data = self\n            .buttons\n            .entry(btn)\n            .or_insert_with(|| ButtonData::new(value, false, false, counter, timestamp));\n        data.value = value;\n        data.counter = counter;\n        data.last_event_ts = timestamp;\n    }\n\n    pub(crate) fn update_axis(&mut self, axis: Code, data: AxisData) {\n        self.axes.insert(axis, data);\n    }\n}\n\n/// Iterator over `ButtonData`.\npub struct ButtonDataIter<'a>(hash_map::Iter<'a, Code, ButtonData>);\n\n/// Iterator over `AxisData`.\npub struct AxisDataIter<'a>(hash_map::Iter<'a, Code, AxisData>);\n\nimpl<'a> Iterator for ButtonDataIter<'a> {\n    type Item = (Code, &'a ButtonData);\n\n    fn next(&mut self) -> Option<Self::Item> {\n        self.0.next().map(|(k, v)| (*k, v))\n    }\n}\n\nimpl<'a> Iterator for AxisDataIter<'a> {\n    type Item = (Code, &'a AxisData);\n\n    fn next(&mut self) -> Option<Self::Item> {\n        self.0.next().map(|(k, v)| (*k, v))\n    }\n}\n\n/// Information about button stored in `State`.\n#[derive(Clone, Copy, Debug)]\npub struct ButtonData {\n    last_event_ts: SystemTime,\n    counter: u64,\n    value: f32,\n    is_pressed: bool,\n    is_repeating: bool,\n}\n\nimpl ButtonData {\n    pub(crate) fn new(\n        value: f32,\n        pressed: bool,\n        repeating: bool,\n        counter: u64,\n        time: SystemTime,\n    ) -> Self {\n        ButtonData {\n            last_event_ts: time,\n            counter,\n            value,\n            is_pressed: pressed,\n            is_repeating: repeating,\n        }\n    }\n\n    /// Returns `true` if button is pressed.\n    pub fn is_pressed(&self) -> bool {\n        self.is_pressed\n    }\n\n    /// Returns value of button.\n    pub fn value(&self) -> f32 {\n        self.value\n    }\n\n    /// Returns `true` if button is repeating.\n    pub fn is_repeating(&self) -> bool {\n        self.is_repeating\n    }\n\n    /// Returns value of counter when button state last changed.\n    pub fn counter(&self) -> u64 {\n        self.counter\n    }\n\n    /// Returns when button state last changed.\n    pub fn timestamp(&self) -> SystemTime {\n        self.last_event_ts\n    }\n}\n\n/// Information about axis stored in `State`.\n#[derive(Clone, Copy, Debug)]\npub struct AxisData {\n    last_event_ts: SystemTime,\n    last_event_c: u64,\n    value: f32,\n}\n\nimpl AxisData {\n    pub(crate) fn new(value: f32, counter: u64, time: SystemTime) -> Self {\n        AxisData {\n            last_event_ts: time,\n            last_event_c: counter,\n            value,\n        }\n    }\n    /// Returns value of axis.\n    pub fn value(&self) -> f32 {\n        self.value\n    }\n\n    /// Returns value of counter when axis value last changed.\n    pub fn counter(&self) -> u64 {\n        self.last_event_c\n    }\n\n    /// Returns when axis value last changed.\n    pub fn timestamp(&self) -> SystemTime {\n        self.last_event_ts\n    }\n}\n"
  },
  {
    "path": "gilrs/src/ff/base_effect.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\n\nuse std::ops::Mul;\n\nuse super::time::Ticks;\n\n/// Kind of [`BaseEffect`](struct.BaseEffect.html).\n///\n/// Currently base effect support only xinput model of force feedback, which means that  gamepad\n/// have weak and strong motor.\n#[derive(Copy, Clone, PartialEq, Eq, Debug)]\n#[non_exhaustive]\npub enum BaseEffectType {\n    Weak { magnitude: u16 },\n    Strong { magnitude: u16 },\n}\n\nimpl BaseEffectType {\n    fn magnitude(&self) -> u16 {\n        match *self {\n            BaseEffectType::Weak { magnitude } => magnitude,\n            BaseEffectType::Strong { magnitude } => magnitude,\n        }\n    }\n}\n\nimpl Mul<f32> for BaseEffectType {\n    type Output = BaseEffectType;\n\n    fn mul(self, rhs: f32) -> Self::Output {\n        let mg = (self.magnitude() as f32 * rhs) as u16;\n        match self {\n            BaseEffectType::Weak { .. } => BaseEffectType::Weak { magnitude: mg },\n            BaseEffectType::Strong { .. } => BaseEffectType::Strong { magnitude: mg },\n        }\n    }\n}\n\nimpl Default for BaseEffectType {\n    fn default() -> Self {\n        BaseEffectType::Weak { magnitude: 0 }\n    }\n}\n\n/// Basic building block used to create more complex force feedback effects.\n///\n/// For each base effect you can specify it's type, for how long should it be played and it's\n/// strength during playback.\n#[derive(Copy, Clone, PartialEq, Debug, Default)]\npub struct BaseEffect {\n    /// Type of base effect.\n    pub kind: BaseEffectType,\n    /// Defines playback duration and delays between each repetition.\n    pub scheduling: Replay,\n    // TODO: maybe allow other f(t)?\n    /// Basic attenuation function.\n    pub envelope: Envelope,\n}\n\nimpl BaseEffect {\n    /// Returns `Weak` or `Strong` after applying envelope.\n    pub(super) fn magnitude_at(&self, ticks: Ticks) -> BaseEffectType {\n        if let Some(wrapped) = self.scheduling.wrap(ticks) {\n            let att =\n                self.scheduling.at(wrapped) * self.envelope.at(wrapped, self.scheduling.play_for);\n            self.kind * att\n        } else {\n            self.kind * 0.0\n        }\n    }\n}\n\n// TODO: Image with \"envelope\"\n#[derive(Copy, Clone, PartialEq, Debug, Default)]\n/// Envelope shaped attenuation(time) function.\npub struct Envelope {\n    pub attack_length: Ticks,\n    pub attack_level: f32,\n    pub fade_length: Ticks,\n    pub fade_level: f32,\n}\n\nimpl Envelope {\n    pub(super) fn at(&self, ticks: Ticks, dur: Ticks) -> f32 {\n        debug_assert!(self.fade_length < dur);\n        debug_assert!(self.attack_length + self.fade_length < dur);\n\n        if ticks < self.attack_length {\n            self.attack_level\n                + ticks.0 as f32 * (1.0 - self.attack_level) / self.attack_length.0 as f32\n        } else if ticks + self.fade_length > dur {\n            1.0 + (ticks + self.fade_length - dur).0 as f32 * (self.fade_level - 1.0)\n                / self.fade_length.0 as f32\n        } else {\n            1.0\n        }\n    }\n}\n\n/// Defines scheduling of the basic force feedback effect.\n///\n/// ```text\n///        ____________            ____________            ____________\n///        |          |            |          |            |\n/// _______|          |____________|          |____________|\n///  after   play_for   with_delay   play_for   with_delay   play_for\n/// ```\n#[derive(Copy, Clone, PartialEq, Eq, Debug)]\npub struct Replay {\n    /// Start playback `after` ticks after `Effect::play()` is called.\n    pub after: Ticks,\n    /// Playback duration.\n    pub play_for: Ticks,\n    /// If playback should be repeated delay it for `with_delay` ticks.\n    pub with_delay: Ticks,\n}\n\nimpl Replay {\n    pub(super) fn at(&self, ticks: Ticks) -> f32 {\n        if ticks >= self.play_for {\n            0.0\n        } else {\n            1.0\n        }\n    }\n\n    /// Returns duration of effect calculated as `play_for + with_delay`.\n    pub fn dur(&self) -> Ticks {\n        self.play_for + self.with_delay\n    }\n\n    /// Returns `None` if effect hasn't started; or wrapped value\n    fn wrap(&self, ticks: Ticks) -> Option<Ticks> {\n        ticks.checked_sub(self.after).map(|t| t % self.dur())\n    }\n}\n\nimpl Default for Replay {\n    fn default() -> Self {\n        Replay {\n            after: Ticks(0),\n            play_for: Ticks(1),\n            with_delay: Ticks(0),\n        }\n    }\n}\n"
  },
  {
    "path": "gilrs/src/ff/effect_source.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\n\nuse std::error::Error;\nuse std::ops::{AddAssign, Mul};\nuse std::{fmt, mem};\n\nuse crate::{Event, EventType, GamepadId};\n\nuse super::base_effect::{BaseEffect, BaseEffectType};\nuse super::time::{Repeat, Ticks};\n\nuse vec_map::VecMap;\n\n/// Specifies how distance between effect source and listener attenuates effect.\n///\n/// They are based on\n/// [OpenAL Specification](http://openal.org/documentation/openal-1.1-specification.pdf) (chapter\n/// 3.4), but the best way to see how they differ is to run `ff_pos` example.\n///\n/// Make sure that all parameters are ≥ 0. Additionally `Linear` and `LinearClamped` models don't\n/// like if `ref_distance == max_distance` while others would prefer `ref_distance > 0`.\n#[derive(Clone, Copy, Debug, Default, PartialEq)]\npub enum DistanceModel {\n    /// Effect is not attenuated by distance.\n    #[default]\n    None,\n    /// Linear distance model.\n    Linear {\n        ref_distance: f32,\n        rolloff_factor: f32,\n        max_distance: f32,\n    },\n    /// Linear distance clamped model.\n    LinearClamped {\n        ref_distance: f32,\n        rolloff_factor: f32,\n        max_distance: f32,\n    },\n    /// Inverse distance model.\n    Inverse {\n        ref_distance: f32,\n        rolloff_factor: f32,\n    },\n    /// Inverse distance clamped model.\n    InverseClamped {\n        ref_distance: f32,\n        rolloff_factor: f32,\n        max_distance: f32,\n    },\n    /// Exponential distance model.\n    Exponential {\n        ref_distance: f32,\n        rolloff_factor: f32,\n    },\n    /// Exponential distance clamped model.\n    ExponentialClamped {\n        ref_distance: f32,\n        rolloff_factor: f32,\n        max_distance: f32,\n    },\n}\n\nimpl DistanceModel {\n    fn attenuation(self, mut distance: f32) -> f32 {\n        // For now we will follow OpenAL[1] specification for distance models. See chapter 3.4 for\n        // more details.\n        //\n        // [1]: http://openal.org/documentation/openal-1.1-specification.pdf\n        match self {\n            DistanceModel::Linear {\n                ref_distance,\n                max_distance,\n                rolloff_factor,\n            } => {\n                distance = distance.min(max_distance);\n\n                1.0 - rolloff_factor * (distance - ref_distance) / (max_distance - ref_distance)\n            }\n            DistanceModel::LinearClamped {\n                ref_distance,\n                max_distance,\n                rolloff_factor,\n            } => {\n                distance = distance.max(ref_distance);\n                distance = distance.min(max_distance);\n\n                1.0 - rolloff_factor * (distance - ref_distance) / (max_distance - ref_distance)\n            }\n            DistanceModel::Inverse {\n                ref_distance,\n                rolloff_factor,\n            } => ref_distance / (ref_distance + rolloff_factor * (distance - ref_distance)),\n            DistanceModel::InverseClamped {\n                ref_distance,\n                max_distance,\n                rolloff_factor,\n            } => {\n                distance = distance.max(ref_distance);\n                distance = distance.min(max_distance);\n\n                ref_distance / (ref_distance + rolloff_factor * (distance - ref_distance))\n            }\n            DistanceModel::Exponential {\n                ref_distance,\n                rolloff_factor,\n            } => (distance / ref_distance).powf(-rolloff_factor),\n            DistanceModel::ExponentialClamped {\n                ref_distance,\n                max_distance,\n                rolloff_factor,\n            } => {\n                distance = distance.max(ref_distance);\n                distance = distance.min(max_distance);\n\n                (distance / ref_distance).powf(-rolloff_factor)\n            }\n            DistanceModel::None => 1.0,\n        }\n    }\n\n    pub(crate) fn validate(self) -> Result<(), DistanceModelError> {\n        let (ref_distance, rolloff_factor, max_distance) = match self {\n            DistanceModel::Inverse {\n                ref_distance,\n                rolloff_factor,\n            } => {\n                if ref_distance <= 0.0 {\n                    return Err(DistanceModelError::InvalidModelParameter);\n                }\n\n                (ref_distance, rolloff_factor, 0.0)\n            }\n            DistanceModel::InverseClamped {\n                ref_distance,\n                max_distance,\n                rolloff_factor,\n            } => {\n                if ref_distance <= 0.0 {\n                    return Err(DistanceModelError::InvalidModelParameter);\n                }\n\n                (ref_distance, rolloff_factor, max_distance)\n            }\n            DistanceModel::Linear {\n                ref_distance,\n                max_distance,\n                rolloff_factor,\n            } => {\n                if ref_distance == max_distance {\n                    return Err(DistanceModelError::InvalidModelParameter);\n                }\n\n                (ref_distance, rolloff_factor, max_distance)\n            }\n            DistanceModel::LinearClamped {\n                ref_distance,\n                max_distance,\n                rolloff_factor,\n            } => {\n                if ref_distance == max_distance {\n                    return Err(DistanceModelError::InvalidModelParameter);\n                }\n\n                (ref_distance, rolloff_factor, max_distance)\n            }\n            DistanceModel::Exponential {\n                ref_distance,\n                rolloff_factor,\n            } => {\n                if ref_distance <= 0.0 {\n                    return Err(DistanceModelError::InvalidModelParameter);\n                }\n\n                (ref_distance, rolloff_factor, 0.0)\n            }\n            DistanceModel::ExponentialClamped {\n                ref_distance,\n                max_distance,\n                rolloff_factor,\n            } => {\n                if ref_distance <= 0.0 {\n                    return Err(DistanceModelError::InvalidModelParameter);\n                }\n\n                (ref_distance, rolloff_factor, max_distance)\n            }\n            DistanceModel::None => (0.0, 0.0, 0.0),\n        };\n\n        if ref_distance < 0.0 {\n            Err(DistanceModelError::InvalidReferenceDistance)\n        } else if rolloff_factor < 0.0 {\n            Err(DistanceModelError::InvalidRolloffFactor)\n        } else if max_distance < 0.0 {\n            Err(DistanceModelError::InvalidMaxDistance)\n        } else {\n            Ok(())\n        }\n    }\n}\n\n/// Error that can be returned when passing [`DistanceModel`](struct.DistanceModel.html) with\n/// invalid value.\n#[derive(Copy, Clone, Debug, PartialEq, Eq)]\n#[non_exhaustive]\npub enum DistanceModelError {\n    /// Reference distance is < 0.\n    InvalidReferenceDistance,\n    /// Rolloff factor is < 0.\n    InvalidRolloffFactor,\n    /// Max distance is < 0.\n    InvalidMaxDistance,\n    /// Possible divide by zero\n    InvalidModelParameter,\n}\n\nimpl Error for DistanceModelError {}\n\nimpl fmt::Display for DistanceModelError {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let s = match self {\n            DistanceModelError::InvalidReferenceDistance => \"reference distance is < 0\",\n            DistanceModelError::InvalidRolloffFactor => \"rolloff factor is < 0\",\n            DistanceModelError::InvalidMaxDistance => \"max distance is < 0\",\n            DistanceModelError::InvalidModelParameter => \"possible divide by zero\",\n        };\n\n        f.write_str(s)\n    }\n}\n\n#[derive(Copy, Clone, PartialEq, Eq, Debug)]\npub(super) enum EffectState {\n    Playing { since: Ticks },\n    Stopped,\n}\n\n#[derive(Clone, PartialEq, Debug)]\npub(crate) struct EffectSource {\n    base_effects: Vec<BaseEffect>,\n    // TODO: Use bitset\n    pub(super) devices: VecMap<()>,\n    pub(super) repeat: Repeat,\n    pub(super) distance_model: DistanceModel,\n    pub(super) position: [f32; 3],\n    pub(super) gain: f32,\n    pub(super) state: EffectState,\n    pub(super) completion_events: Vec<Event>,\n}\n\nimpl EffectSource {\n    pub(super) fn new(\n        base_effects: Vec<BaseEffect>,\n        devices: VecMap<()>,\n        repeat: Repeat,\n        dist_model: DistanceModel,\n        position: [f32; 3],\n        gain: f32,\n    ) -> Self {\n        EffectSource {\n            base_effects,\n            devices,\n            repeat,\n            distance_model: dist_model,\n            position,\n            gain,\n            state: EffectState::Stopped,\n            completion_events: vec![],\n        }\n    }\n\n    pub(super) fn combine_base_effects(&mut self, ticks: Ticks, actor_pos: [f32; 3]) -> Magnitude {\n        let ticks = match self.state {\n            EffectState::Playing { since } => {\n                debug_assert!(ticks >= since);\n                ticks - since\n            }\n            EffectState::Stopped => return Magnitude::zero(),\n        };\n\n        match self.repeat {\n            Repeat::For(max_dur) if ticks > max_dur => {\n                self.state = EffectState::Stopped;\n                self.devices.keys().for_each(|id| {\n                    let event = Event::new(GamepadId(id), EventType::ForceFeedbackEffectCompleted);\n                    self.completion_events.push(event);\n                });\n            }\n            _ => (),\n        }\n\n        let attenuation = self\n            .distance_model\n            .attenuation(self.position.distance(actor_pos))\n            * self.gain;\n        if attenuation < 0.05 {\n            return Magnitude::zero();\n        }\n\n        let mut final_magnitude = Magnitude::zero();\n        for effect in &self.base_effects {\n            match effect.magnitude_at(ticks) {\n                BaseEffectType::Strong { magnitude } => {\n                    final_magnitude.strong = final_magnitude.strong.saturating_add(magnitude)\n                }\n                BaseEffectType::Weak { magnitude } => {\n                    final_magnitude.weak = final_magnitude.weak.saturating_add(magnitude)\n                }\n            };\n        }\n        final_magnitude * attenuation\n    }\n\n    pub(super) fn flush_completion_events(&mut self) -> Vec<Event> {\n        mem::take(&mut self.completion_events)\n    }\n}\n\n/// (strong, weak) pair.\n#[derive(Copy, Clone, Debug)]\npub(super) struct Magnitude {\n    pub strong: u16,\n    pub weak: u16,\n}\n\nimpl Magnitude {\n    pub fn zero() -> Self {\n        Magnitude { strong: 0, weak: 0 }\n    }\n}\n\nimpl Mul<f32> for Magnitude {\n    type Output = Magnitude;\n\n    fn mul(self, rhs: f32) -> Self::Output {\n        debug_assert!(rhs >= 0.0);\n        let strong = self.strong as f32 * rhs;\n        let strong = if strong > u16::MAX as f32 {\n            u16::MAX\n        } else {\n            strong as u16\n        };\n        let weak = self.weak as f32 * rhs;\n        let weak = if weak > u16::MAX as f32 {\n            u16::MAX\n        } else {\n            weak as u16\n        };\n        Magnitude { strong, weak }\n    }\n}\n\nimpl AddAssign for Magnitude {\n    fn add_assign(&mut self, rhs: Magnitude) {\n        self.strong = self.strong.saturating_add(rhs.strong);\n        self.weak = self.weak.saturating_add(rhs.weak);\n    }\n}\n\ntrait SliceVecExt {\n    type Base;\n\n    fn distance(self, from: Self) -> Self::Base;\n}\n\nimpl SliceVecExt for [f32; 3] {\n    type Base = f32;\n\n    fn distance(self, from: Self) -> f32 {\n        ((from[0] - self[0]).powi(2) + (from[1] - self[1]).powi(2) + (from[2] - self[2]).powi(2))\n            .sqrt()\n    }\n}\n"
  },
  {
    "path": "gilrs/src/ff/mod.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\n\n// This code is not used on wasm\n#![cfg_attr(target_arch = \"wasm32\", allow(dead_code))]\n\n//! Force feedback module.\n//!\n//! To use force feedback, you have to create one or more [`Effect`s](struct.Effect.html). Each\n//! `Effect` contains one or more [`BasicEffect`s](struct.BasicEffect.html) and parameters that\n//! describe effect's source, like it's position, gain or used\n//! [`DistanceModel`](enum.DistanceModel.html). Final strength of effect is based on saturating sum\n//! (to `u16::MAX`) of all base effects and time from the start of playback, attenuation from\n//! distance between effect source and listener (represented by gamepad) and effect's gain.\n//!\n//! See also [`Gilrs::set_listener_position()`](../struct.Gilrs.html#method.set_listener_position)\n//! and [`Gamepad::is_ff_supported()`](../struct.Gamepad.html#method.is_ff_supported).\n//!\n//! # Example\n//!\n//! ```rust\n//! use gilrs::Gilrs;\n//! use gilrs::ff::{EffectBuilder, Replay, BaseEffect, BaseEffectType, Ticks};\n//!\n//! let mut gilrs = Gilrs::new().unwrap();\n//! let support_ff = gilrs\n//!     .gamepads()\n//!     .filter_map(|(id, gp)| if gp.is_ff_supported() { Some(id) } else { None })\n//!     .collect::<Vec<_>>();\n//!\n//! let duration = Ticks::from_ms(150);\n//! let effect = EffectBuilder::new()\n//!     .add_effect(BaseEffect {\n//!         kind: BaseEffectType::Strong { magnitude: 60_000 },\n//!         scheduling: Replay { play_for: duration, with_delay: duration * 3, ..Default::default() },\n//!         envelope: Default::default(),\n//!     })\n//!     .add_effect(BaseEffect {\n//!         kind: BaseEffectType::Weak { magnitude: 60_000 },\n//!         scheduling: Replay { after: duration * 2, play_for: duration, with_delay: duration * 3 },\n//!         ..Default::default()\n//!     })\n//!     .gamepads(&support_ff)\n//!     .finish(&mut gilrs).unwrap();\n//!\n//! effect.play().unwrap();\n//! ```\n//!\n//! See [`examples/ff_pos.rs`](https://gitlab.com/gilrs-project/gilrs/blob/v0.11.0/examples/ff_pos.rs) for\n//! more advanced example.\nmod base_effect;\nmod effect_source;\npub(crate) mod server;\nmod time;\n\npub use self::base_effect::{BaseEffect, BaseEffectType, Envelope, Replay};\npub use self::effect_source::{DistanceModel, DistanceModelError};\n#[allow(unused_imports)]\npub(crate) use self::time::TICK_DURATION;\npub use self::time::{Repeat, Ticks};\n\nuse std::error::Error as StdError;\nuse std::hash::{Hash, Hasher};\nuse std::sync::mpsc::{SendError, Sender};\nuse std::{f32, fmt};\n\nuse self::effect_source::EffectSource;\nuse crate::ff::server::Message;\nuse crate::gamepad::{Gamepad, GamepadId, Gilrs};\nuse crate::utils;\n\nuse vec_map::VecMap;\n\n/// Handle to force feedback effect.\n///\n/// `Effect` represents force feedback effect that can be played on one or more gamepads. It uses a\n/// form of reference counting, so it can be cheaply cloned. To create new `Effect` use\n/// [`EffectBuilder`](struct.EffectBuilder.html).\n///\n/// All methods on can return `Error::SendFailed` although it shouldn't normally happen.\npub struct Effect {\n    id: usize,\n    tx: Sender<Message>,\n}\n\nimpl PartialEq for Effect {\n    fn eq(&self, other: &Effect) -> bool {\n        self.id == other.id\n    }\n}\n\nimpl Eq for Effect {}\n\nimpl Hash for Effect {\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        self.id.hash(state);\n    }\n}\n\nimpl Clone for Effect {\n    fn clone(&self) -> Self {\n        let _ = self.tx.send(Message::HandleCloned { id: self.id });\n        Effect {\n            id: self.id,\n            tx: self.tx.clone(),\n        }\n    }\n}\n\nimpl Drop for Effect {\n    fn drop(&mut self) {\n        let _ = self.tx.send(Message::HandleDropped { id: self.id });\n    }\n}\n\nimpl Effect {\n    /// Plays effect on all associated gamepads.\n    pub fn play(&self) -> Result<(), Error> {\n        self.tx.send(Message::Play { id: self.id })?;\n\n        Ok(())\n    }\n\n    pub fn stop(&self) -> Result<(), Error> {\n        self.tx.send(Message::Stop { id: self.id })?;\n\n        Ok(())\n    }\n\n    /// Changes gamepads that are associated with effect. Effect will be only played on gamepads\n    /// from last call to this function.\n    ///\n    /// # Errors\n    ///\n    /// Returns `Error::Disconnected(id)` or `Error::FfNotSupported(id)` on first gamepad in `ids`\n    /// that is disconnected or doesn't support force feedback.\n    pub fn set_gamepads(&self, ids: &[GamepadId], gilrs: &Gilrs) -> Result<(), Error> {\n        let mut gamepads = VecMap::new();\n\n        for dev in ids.iter().cloned() {\n            if !gilrs\n                .connected_gamepad(dev)\n                .ok_or(Error::Disconnected(dev))?\n                .is_ff_supported()\n            {\n                return Err(Error::FfNotSupported(dev));\n            } else {\n                gamepads.insert(dev.0, ());\n            }\n        }\n\n        self.tx.send(Message::SetGamepads {\n            id: self.id,\n            gamepads,\n        })?;\n\n        Ok(())\n    }\n\n    /// Adds gamepad to the list of gamepads associated with effect.\n    ///\n    /// # Errors\n    ///\n    /// Returns `Error::Disconnected(id)` or `Error::FfNotSupported(id)` if gamepad is not connected\n    /// or does not support force feedback.\n    pub fn add_gamepad(&self, gamepad: &Gamepad<'_>) -> Result<(), Error> {\n        if !gamepad.is_connected() {\n            Err(Error::Disconnected(gamepad.id()))\n        } else if !gamepad.is_ff_supported() {\n            Err(Error::FfNotSupported(gamepad.id()))\n        } else {\n            self.tx.send(Message::AddGamepad {\n                id: self.id,\n                gamepad_id: gamepad.id(),\n            })?;\n\n            Ok(())\n        }\n    }\n\n    /// Changes what should happen to effect when it ends.\n    pub fn set_repeat(&self, repeat: Repeat) -> Result<(), Error> {\n        self.tx.send(Message::SetRepeat {\n            id: self.id,\n            repeat,\n        })?;\n\n        Ok(())\n    }\n\n    /// Changes distance model associated with effect.\n    ///\n    /// # Errors\n    ///\n    /// Returns `Error::InvalidDistanceModel` if `model` is not valid. See\n    /// [`DistanceModel`](enum.DistanceModelError.html) for details.\n    pub fn set_distance_model(&self, model: DistanceModel) -> Result<(), Error> {\n        model.validate()?;\n        self.tx\n            .send(Message::SetDistanceModel { id: self.id, model })?;\n\n        Ok(())\n    }\n\n    /// Changes position of the source of effect.\n    pub fn set_position<Vec3f: Into<[f32; 3]>>(&self, position: Vec3f) -> Result<(), Error> {\n        let position = position.into();\n        self.tx.send(Message::SetPosition {\n            id: self.id,\n            position,\n        })?;\n\n        Ok(())\n    }\n\n    /// Changes gain of the effect. `gain` will be clamped to \\[0.0, f32::MAX\\].\n    pub fn set_gain(&self, gain: f32) -> Result<(), Error> {\n        let gain = utils::clamp(gain, 0.0, f32::MAX);\n        self.tx.send(Message::SetGain { id: self.id, gain })?;\n\n        Ok(())\n    }\n}\n\n/// Creates new [`Effect`](struct.Effect.html).\n#[derive(Clone, PartialEq, Debug)]\npub struct EffectBuilder {\n    base_effects: Vec<BaseEffect>,\n    devices: VecMap<()>,\n    repeat: Repeat,\n    dist_model: DistanceModel,\n    position: [f32; 3],\n    gain: f32,\n}\n\nimpl EffectBuilder {\n    /// Creates new builder with following defaults: no gamepads, no base effects, repeat set to\n    /// infinitely, no distance model, position in (0.0, 0.0, 0.0) and gain 1.0. Use `finish()` to\n    /// create new effect.\n    pub fn new() -> Self {\n        EffectBuilder {\n            base_effects: Vec::new(),\n            devices: VecMap::new(),\n            repeat: Repeat::Infinitely,\n            dist_model: DistanceModel::None,\n            position: [0.0, 0.0, 0.0],\n            gain: 1.0,\n        }\n    }\n\n    /// Adds new [`BaseEffect`](struct.BaseEffect.html).\n    pub fn add_effect(&mut self, effect: BaseEffect) -> &mut Self {\n        self.base_effects.push(effect);\n        self\n    }\n\n    /// Changes gamepads that are associated with effect. Effect will be only played on gamepads\n    /// from last call to this function.\n    pub fn gamepads(&mut self, ids: &[GamepadId]) -> &mut Self {\n        for dev in ids {\n            self.devices.insert(dev.0, ());\n        }\n        self\n    }\n\n    /// Adds gamepad to the list of gamepads associated with effect.\n    pub fn add_gamepad(&mut self, gamepad: &Gamepad<'_>) -> &mut Self {\n        self.devices.insert(gamepad.id().0, ());\n\n        self\n    }\n\n    /// Changes what should happen to effect when it ends.\n    pub fn repeat(&mut self, repeat: Repeat) -> &mut Self {\n        self.repeat = repeat;\n        self\n    }\n\n    /// Changes distance model associated with effect.\n    pub fn distance_model(&mut self, model: DistanceModel) -> &mut Self {\n        self.dist_model = model;\n        self\n    }\n\n    /// Changes position of the source of effect.\n    pub fn position<Vec3f: Into<[f32; 3]>>(&mut self, position: Vec3f) -> &mut Self {\n        self.position = position.into();\n        self\n    }\n\n    /// Changes gain of the effect. `gain` will be clamped to \\[0.0, f32::MAX\\].\n    pub fn gain(&mut self, gain: f32) -> &mut Self {\n        self.gain = utils::clamp(gain, 0.0, f32::MAX);\n        self\n    }\n\n    /// Validates all parameters and creates new effect.\n    ///\n    /// # Errors\n    ///\n    /// Returns `Error::Disconnected(id)` or `Error::FfNotSupported(id)` on first gamepad in `ids`\n    /// that is disconnected or doesn't support force feedback.\n    ///\n    /// Returns `Error::InvalidDistanceModel` if `model` is not valid. See\n    /// [`DistanceModel`](enum.DistanceModelError.html) for details.\n    pub fn finish(&mut self, gilrs: &mut Gilrs) -> Result<Effect, Error> {\n        for (dev, _) in &self.devices {\n            let dev = GamepadId(dev);\n            if !gilrs\n                .connected_gamepad(dev)\n                .ok_or(Error::Disconnected(dev))?\n                .is_ff_supported()\n            {\n                return Err(Error::FfNotSupported(dev));\n            }\n        }\n\n        self.dist_model.validate()?;\n\n        let effect = EffectSource::new(\n            self.base_effects.clone(),\n            self.devices.clone(),\n            self.repeat,\n            self.dist_model,\n            self.position,\n            self.gain,\n        );\n        let id = gilrs.next_ff_id();\n        let tx = gilrs.ff_sender();\n        tx.send(Message::Create {\n            id,\n            effect: Box::new(effect),\n        })?;\n        Ok(Effect { id, tx: tx.clone() })\n    }\n}\n\nimpl Default for EffectBuilder {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\n/// Basic error type in force feedback module.\n#[derive(Copy, Clone, Debug, PartialEq, Eq)]\n#[non_exhaustive]\npub enum Error {\n    /// Force feedback is not supported by device with this ID\n    FfNotSupported(GamepadId),\n    /// Device is not connected\n    Disconnected(GamepadId),\n    /// Distance model is invalid.\n    InvalidDistanceModel(DistanceModelError),\n    /// The other end of channel was dropped.\n    SendFailed,\n    /// Unexpected error has occurred\n    Other,\n}\n\nimpl StdError for Error {\n    fn source(&self) -> Option<&(dyn StdError + 'static)> {\n        match self {\n            Error::InvalidDistanceModel(m) => Some(m),\n            _ => None,\n        }\n    }\n}\n\nimpl fmt::Display for Error {\n    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let sbuf;\n        let s = match self {\n            Error::FfNotSupported(id) => {\n                sbuf = format!(\n                    \"force feedback is not supported by device with id {}.\",\n                    id.0\n                );\n                sbuf.as_ref()\n            }\n            Error::Disconnected(id) => {\n                sbuf = format!(\"device with id {} is not connected.\", id.0);\n                sbuf.as_ref()\n            }\n            Error::InvalidDistanceModel(_) => \"distance model is invalid\",\n            Error::SendFailed => \"receiving end of a channel is disconnected.\",\n            Error::Other => \"unespected error has occurred.\",\n        };\n\n        fmt.write_str(s)\n    }\n}\n\nimpl<T> From<SendError<T>> for Error {\n    fn from(_: SendError<T>) -> Self {\n        Error::SendFailed\n    }\n}\n\nimpl From<DistanceModelError> for Error {\n    fn from(f: DistanceModelError) -> Self {\n        Error::InvalidDistanceModel(f)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn envelope() {\n        let env = Envelope {\n            attack_length: Ticks(10),\n            attack_level: 0.2,\n            fade_length: Ticks(10),\n            fade_level: 0.2,\n        };\n        let dur = Ticks(40);\n\n        assert_eq!(env.at(Ticks(0), dur), 0.2);\n        assert_eq!(env.at(Ticks(5), dur), 0.6);\n        assert_eq!(env.at(Ticks(10), dur), 1.0);\n        assert_eq!(env.at(Ticks(20), dur), 1.0);\n        assert_eq!(env.at(Ticks(30), dur), 1.0);\n        assert_eq!(env.at(Ticks(35), dur), 0.6);\n        assert_eq!(env.at(Ticks(40), dur), 0.19999999);\n    }\n\n    #[test]\n    fn envelope_default() {\n        let env = Envelope::default();\n        let dur = Ticks(40);\n\n        assert_eq!(env.at(Ticks(0), dur), 1.0);\n        assert_eq!(env.at(Ticks(20), dur), 1.0);\n        assert_eq!(env.at(Ticks(40), dur), 1.0);\n    }\n\n    #[test]\n    fn replay() {\n        let replay = Replay {\n            after: Ticks(10),\n            play_for: Ticks(50),\n            with_delay: Ticks(20),\n        };\n\n        assert_eq!(replay.at(Ticks(0)), 1.0);\n        assert_eq!(replay.at(Ticks(9)), 1.0);\n        assert_eq!(replay.at(Ticks(10)), 1.0);\n        assert_eq!(replay.at(Ticks(30)), 1.0);\n        assert_eq!(replay.at(Ticks(59)), 0.0);\n        assert_eq!(replay.at(Ticks(60)), 0.0);\n        assert_eq!(replay.at(Ticks(70)), 0.0);\n    }\n}\n"
  },
  {
    "path": "gilrs/src/ff/server.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\n\nuse super::effect_source::{DistanceModel, EffectSource, EffectState, Magnitude};\nuse super::time::{Repeat, Ticks, TICK_DURATION};\n\nuse std::ops::{Deref, DerefMut};\nuse std::sync::mpsc::{self, Receiver, Sender};\nuse std::thread;\nuse std::time::{Duration, Instant};\n\nuse crate::gamepad::GamepadId;\nuse crate::Event;\nuse gilrs_core::FfDevice;\n\nuse vec_map::VecMap;\n\n#[derive(Debug)]\npub(crate) enum Message {\n    Create {\n        id: usize,\n        effect: Box<EffectSource>,\n    },\n    HandleCloned {\n        id: usize,\n    },\n    HandleDropped {\n        id: usize,\n    },\n    Play {\n        id: usize,\n    },\n    Stop {\n        id: usize,\n    },\n    Open {\n        id: usize,\n        device: FfDevice,\n    },\n    Close {\n        id: usize,\n    },\n    SetListenerPosition {\n        id: usize,\n        position: [f32; 3],\n    },\n    SetGamepads {\n        id: usize,\n        gamepads: VecMap<()>,\n    },\n    AddGamepad {\n        id: usize,\n        gamepad_id: GamepadId,\n    },\n    SetRepeat {\n        id: usize,\n        repeat: Repeat,\n    },\n    SetDistanceModel {\n        id: usize,\n        model: DistanceModel,\n    },\n    SetPosition {\n        id: usize,\n        position: [f32; 3],\n    },\n    SetGain {\n        id: usize,\n        gain: f32,\n    },\n}\n\npub(crate) enum FfMessage {\n    EffectCompleted { event: Event },\n}\n\nimpl Message {\n    // Whether to use trace level logging or debug\n    fn use_trace_level(&self) -> bool {\n        use self::Message::*;\n\n        matches!(\n            self,\n            &SetListenerPosition { .. } | &HandleCloned { .. } | &HandleDropped { .. }\n        )\n    }\n}\n\n#[derive(Debug)]\nstruct Device {\n    inner: FfDevice,\n    position: [f32; 3],\n}\n\nstruct Effect {\n    source: EffectSource,\n    /// Number of created effect's handles.\n    count: usize,\n}\n\nimpl Effect {\n    fn inc(&mut self) -> usize {\n        self.count += 1;\n        self.count\n    }\n\n    fn dec(&mut self) -> usize {\n        self.count -= 1;\n        self.count\n    }\n}\n\nimpl From<EffectSource> for Effect {\n    fn from(source: EffectSource) -> Self {\n        Effect { source, count: 1 }\n    }\n}\n\nimpl Deref for Effect {\n    type Target = EffectSource;\n\n    fn deref(&self) -> &Self::Target {\n        &self.source\n    }\n}\n\nimpl DerefMut for Effect {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.source\n    }\n}\n\nimpl From<FfDevice> for Device {\n    fn from(inner: FfDevice) -> Self {\n        Device {\n            inner,\n            position: [0.0, 0.0, 0.0],\n        }\n    }\n}\n\npub(crate) fn run(tx: Sender<FfMessage>, rx: Receiver<Message>) {\n    let mut effects = VecMap::<Effect>::new();\n    let mut devices = VecMap::<Device>::new();\n    let sleep_dur = Duration::from_millis(TICK_DURATION.into());\n    let mut tick = Ticks(0);\n    let mut completion_events = Vec::<Event>::new();\n\n    loop {\n        let t1 = Instant::now();\n        while let Ok(ev) = rx.try_recv() {\n            if ev.use_trace_level() {\n                trace!(\"New ff event: {:?}\", ev);\n            } else {\n                debug!(\"New ff event: {:?}\", ev);\n            }\n\n            match ev {\n                Message::Create { id, effect } => {\n                    effects.insert(id, (*effect).into());\n                }\n                Message::Play { id } => {\n                    if let Some(effect) = effects.get_mut(id) {\n                        effect.source.state = EffectState::Playing { since: tick }\n                    } else {\n                        error!(\"{:?} with wrong ID\", ev);\n                    }\n                }\n                Message::Stop { id } => {\n                    if let Some(effect) = effects.get_mut(id) {\n                        effect.source.state = EffectState::Stopped\n                    } else {\n                        error!(\"{:?} with wrong ID\", ev);\n                    }\n                }\n                Message::Open { id, device } => {\n                    devices.insert(id, device.into());\n                }\n                Message::Close { id } => {\n                    devices.remove(id);\n                }\n                Message::SetListenerPosition { id, position } => {\n                    if let Some(device) = devices.get_mut(id) {\n                        device.position = position;\n                    } else {\n                        error!(\"{:?} with wrong ID\", ev);\n                    }\n                }\n                Message::HandleCloned { id } => {\n                    if let Some(effect) = effects.get_mut(id) {\n                        effect.inc();\n                    } else {\n                        error!(\"{:?} with wrong ID\", ev);\n                    }\n                }\n                Message::HandleDropped { id } => {\n                    let mut drop = false;\n                    if let Some(effect) = effects.get_mut(id) {\n                        if effect.dec() == 0 {\n                            drop = true;\n                        }\n                    } else {\n                        error!(\"{:?} with wrong ID\", ev);\n                    }\n\n                    if drop {\n                        effects.remove(id);\n                    }\n                }\n                Message::SetGamepads { id, gamepads } => {\n                    if let Some(eff) = effects.get_mut(id) {\n                        eff.source.devices = gamepads;\n                    } else {\n                        error!(\"Invalid effect id {} when changing gamepads.\", id);\n                    }\n                }\n                Message::AddGamepad { id, gamepad_id } => {\n                    if let Some(eff) = effects.get_mut(id) {\n                        eff.source.devices.insert(gamepad_id.0, ());\n                    } else {\n                        error!(\"Invalid effect id {} when changing gamepads.\", id);\n                    }\n                }\n                Message::SetRepeat { id, repeat } => {\n                    if let Some(eff) = effects.get_mut(id) {\n                        eff.source.repeat = repeat;\n                    } else {\n                        error!(\"Invalid effect id {} when changing repeat mode.\", id);\n                    }\n                }\n                Message::SetDistanceModel { id, model } => {\n                    if let Some(eff) = effects.get_mut(id) {\n                        eff.source.distance_model = model;\n                    } else {\n                        error!(\"Invalid effect id {} when changing distance model.\", id);\n                    }\n                }\n                Message::SetPosition { id, position } => {\n                    if let Some(eff) = effects.get_mut(id) {\n                        eff.source.position = position;\n                    } else {\n                        error!(\"Invalid effect id {}.\", id);\n                    }\n                }\n                Message::SetGain { id, gain } => {\n                    if let Some(eff) = effects.get_mut(id) {\n                        eff.source.gain = gain;\n                    } else {\n                        error!(\"Invalid effect id {} when changing effect gain.\", id);\n                    }\n                }\n            }\n        }\n\n        combine_and_play(&mut effects, &mut devices, tick, &mut completion_events);\n        completion_events.iter().for_each(|ev| {\n            let _ = tx.send(FfMessage::EffectCompleted { event: *ev });\n        });\n        completion_events.clear();\n\n        let dur = Instant::now().duration_since(t1);\n        if dur > sleep_dur {\n            // TODO: Should we add dur - sleep_dur to next iteration's dur?\n            warn!(\n                \"One iteration of a force feedback loop took more than {}ms!\",\n                TICK_DURATION\n            );\n        } else {\n            thread::sleep(sleep_dur - dur);\n        }\n        tick.inc();\n    }\n}\n\npub(crate) fn init() -> (Sender<Message>, Receiver<FfMessage>) {\n    let (tx, _rx) = mpsc::channel();\n    let (_tx2, rx2) = mpsc::channel();\n\n    // Wasm doesn't support threads and force feedback\n    #[cfg(not(target_arch = \"wasm32\"))]\n    std::thread::Builder::new()\n        .name(\"gilrs\".to_owned())\n        .spawn(move || run(_tx2, _rx))\n        .expect(\"failed to spawn thread\");\n\n    (tx, rx2)\n}\n\nfn combine_and_play(\n    effects: &mut VecMap<Effect>,\n    devices: &mut VecMap<Device>,\n    tick: Ticks,\n    completion_events: &mut Vec<Event>,\n) {\n    for (dev_id, dev) in devices {\n        let mut magnitude = Magnitude::zero();\n        for (_, ref mut effect) in effects.iter_mut() {\n            if effect.devices.contains_key(dev_id) {\n                magnitude += effect.combine_base_effects(tick, dev.position);\n                completion_events.extend(effect.flush_completion_events());\n            }\n        }\n        trace!(\n            \"({:?}) Setting ff state of {:?} to {:?}\",\n            tick,\n            dev,\n            magnitude\n        );\n        dev.inner.set_ff_state(\n            magnitude.strong,\n            magnitude.weak,\n            Duration::from_millis(u64::from(TICK_DURATION) * 2),\n        );\n    }\n}\n"
  },
  {
    "path": "gilrs/src/ff/time.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\n\nuse std::ops::{Add, AddAssign, Mul, MulAssign, Rem, Sub, SubAssign};\nuse std::time::Duration;\n\nuse crate::utils;\n\npub(crate) const TICK_DURATION: u32 = 50;\n\n/// Represents duration.\n///\n/// This type is only useful as input parameter for other functions in force feedback module. To\n/// create it, use `from_ms()` method. Keep in mind that `Ticks` **is not precise** representation\n/// of time.\n///\n/// # Example\n///\n/// ```rust\n/// use gilrs::ff::Ticks;\n/// use std::time::Duration;\n///\n/// let t1 = Ticks::from_ms(110);\n/// let t2 = Ticks::from(Duration::from_millis(130));\n///\n/// /// `Ticks` is not precise.\n/// assert_eq!(t1, t2);\n/// ```\n#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Default)]\npub struct Ticks(pub(super) u32);\n\nimpl Ticks {\n    pub fn from_ms(dur: u32) -> Self {\n        Ticks(utils::ceil_div(dur, TICK_DURATION))\n    }\n\n    pub(super) fn inc(&mut self) {\n        self.0 += 1\n    }\n\n    pub(super) fn checked_sub(self, rhs: Ticks) -> Option<Ticks> {\n        self.0.checked_sub(rhs.0).map(Ticks)\n    }\n}\n\nimpl From<Duration> for Ticks {\n    fn from(dur: Duration) -> Self {\n        Ticks::from_ms(dur.as_secs() as u32 * 1000 + dur.subsec_millis())\n    }\n}\n\nimpl Add for Ticks {\n    type Output = Ticks;\n\n    fn add(self, rhs: Ticks) -> Self::Output {\n        Ticks(self.0 + rhs.0)\n    }\n}\n\nimpl AddAssign for Ticks {\n    fn add_assign(&mut self, rhs: Ticks) {\n        self.0 += rhs.0\n    }\n}\n\nimpl Sub for Ticks {\n    type Output = Ticks;\n\n    fn sub(self, rhs: Ticks) -> Self::Output {\n        Ticks(self.0 - rhs.0)\n    }\n}\n\nimpl SubAssign for Ticks {\n    fn sub_assign(&mut self, rhs: Ticks) {\n        self.0 -= rhs.0\n    }\n}\n\nimpl Mul<u32> for Ticks {\n    type Output = Ticks;\n\n    fn mul(self, rhs: u32) -> Self::Output {\n        Ticks(self.0 * rhs)\n    }\n}\n\nimpl MulAssign<u32> for Ticks {\n    fn mul_assign(&mut self, rhs: u32) {\n        self.0 *= rhs;\n    }\n}\n\nimpl Rem for Ticks {\n    type Output = Ticks;\n\n    fn rem(self, rhs: Ticks) -> Self::Output {\n        Ticks(self.0 % rhs.0)\n    }\n}\n\n/// Describes how long effect should be played.\n#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]\npub enum Repeat {\n    /// Play effect until stop() is called.\n    #[default]\n    Infinitely,\n    /// Play effect for specified time.\n    For(Ticks),\n}\n"
  },
  {
    "path": "gilrs/src/gamepad.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\n\nuse crate::{\n    ev::{\n        state::{AxisData, ButtonData, GamepadState},\n        Axis, AxisOrBtn, Button, Code, Event, EventType,\n    },\n    ff::{\n        server::{self, FfMessage, Message},\n        Error as FfError,\n    },\n    mapping::{Mapping, MappingData, MappingDb},\n    utils, MappingError,\n};\n\nuse gilrs_core::{\n    self, AxisInfo, Error as PlatformError, Event as RawEvent, EventType as RawEventType,\n};\n\nuse uuid::Uuid;\n\nuse std::cmp::Ordering;\nuse std::{\n    collections::VecDeque,\n    error,\n    fmt::{self, Display},\n    sync::mpsc::{Receiver, Sender},\n    time::Duration,\n};\n\npub use gilrs_core::PowerInfo;\n\n#[cfg(feature = \"serde-serialize\")]\nuse serde::{Deserialize, Serialize};\n\nconst DEFAULT_DEADZONE: f32 = 0.1;\n\n/// Main object responsible of managing gamepads.\n///\n/// In order to get gamepad handle, use `gamepad()`, or `connected_gamepad()`. The main difference\n/// between these two is that `gamepad()` will also return handle to gamepad that is currently\n/// disconnected. However, both functions will return `None` if gamepad with given id has never\n/// existed.\n///\n/// # Event loop\n///\n/// All interesting actions like button was pressed or new controller was connected are represented\n/// by struct [`Event`](struct.Event.html). Use `next_event()` function to retrieve event from\n/// queue.\n///\n/// ```\n/// use gilrs::{Gilrs, Event, EventType, Button};\n///\n/// let mut gilrs = Gilrs::new().unwrap();\n///\n/// // Event loop\n/// loop {\n///     while let Some(event) = gilrs.next_event() {\n///         match event {\n///             Event { id, event: EventType::ButtonPressed(Button::South, _), .. } => {\n///                 println!(\"Player {}: jump!\", id)\n///             }\n///             Event { id, event: EventType::Disconnected, .. } => {\n///                 println!(\"We lost player {}\", id)\n///             }\n///             _ => (),\n///         };\n///     }\n///     # break;\n/// }\n/// ```\n///\n/// # Cached gamepad state\n///\n/// `Gilrs` also menage cached gamepad state. Updating state is done automatically, unless it's\n///  disabled by `GilrsBuilder::set_update_state(false)`. However, if you are using custom filters,\n/// you still have to update state manually – to do this call `update()` method.\n///\n/// To access state you can use `Gamepad::state()` function. Gamepad also implement some state\n/// related functions directly, see [`Gamepad`](struct.Gamepad.html) for more.\n///\n/// ## Counter\n///\n/// `Gilrs` has additional functionality, referred here as *counter*. The idea behind it is simple,\n/// each time you end iteration of update loop, you call `Gilrs::inc()` which will increase\n/// internal counter by one. When state of one if elements changes, value of counter is saved. When\n/// checking state of one of elements you can tell exactly when this event happened. Timestamps are\n/// not good solution here because they can tell you when *system* observed event, not when you\n/// processed it. On the other hand, they are good when you want to implement key repeat or software\n/// debouncing.\n///\n/// ```\n/// use gilrs::{Gilrs, Button};\n///\n/// let mut gilrs = Gilrs::new().unwrap();\n/// let mut player_one = None;\n///\n/// loop {\n///     while let Some(ev) = gilrs.next_event() {\n///         if player_one.is_none() {\n///             player_one = Some(ev.id);\n///         }\n///\n///         // Do other things with event\n///     }\n///\n///     if let Some(id) = player_one {\n///         let gamepad = gilrs.gamepad(id);\n///\n///         if gamepad.is_pressed(Button::DPadLeft) {\n///             // go left\n///         }\n///\n///         match gamepad.button_data(Button::South) {\n///             Some(d) if d.is_pressed() && d.counter() == gilrs.counter() => {\n///                 // jump only if button was observed to be pressed in this iteration\n///             }\n///             _ => ()\n///         }\n///     }\n///\n///     // Increase counter\n///     gilrs.inc();\n/// #   break;\n/// }\n///\n#[derive(Debug)]\npub struct Gilrs {\n    inner: gilrs_core::Gilrs,\n    next_id: usize,\n    tx: Sender<Message>,\n    rx: Receiver<FfMessage>,\n    counter: u64,\n    mappings: MappingDb,\n    default_filters: bool,\n    events: VecDeque<Event>,\n    axis_to_btn_pressed: f32,\n    axis_to_btn_released: f32,\n    pub(crate) update_state: bool,\n    pub(crate) gamepads_data: Vec<GamepadData>,\n}\n\nimpl Gilrs {\n    /// Creates new `Gilrs` with default settings. See [`GilrsBuilder`](struct.GilrsBuilder.html)\n    /// for more details.\n    pub fn new() -> Result<Self, Error> {\n        GilrsBuilder::new().build()\n    }\n\n    /// Returns next pending event. If there is no pending event, `None` is\n    /// returned. This function will not block current thread and should be safe\n    /// to call in async context. Doesn't block the thread it is run in\n    pub fn next_event(&mut self) -> Option<Event> {\n        self.next_event_inner(false, None)\n    }\n\n    /// Same as [Gilrs::next_event], but blocks the thread it is run in. Useful\n    /// for apps that aren't run inside a loop and just react to the user's input,\n    /// like GUI apps.\n    ///\n    /// ## Platform support\n    ///\n    /// This function is not supported on web and will always panic.\n    pub fn next_event_blocking(&mut self, timeout: Option<Duration>) -> Option<Event> {\n        self.next_event_inner(true, timeout)\n    }\n\n    fn next_event_inner(\n        &mut self,\n        is_blocking: bool,\n        blocking_timeout: Option<Duration>,\n    ) -> Option<Event> {\n        use crate::ev::filter::{axis_dpad_to_button, deadzone, Filter, Jitter};\n\n        let ev = if self.default_filters {\n            let jitter_filter = Jitter::new();\n            loop {\n                let ev = self\n                    .next_event_priv(is_blocking, blocking_timeout)\n                    .filter_ev(&axis_dpad_to_button, self)\n                    .filter_ev(&jitter_filter, self)\n                    .filter_ev(&deadzone, self);\n\n                // Skip all dropped events, there is no reason to return them\n                match ev {\n                    Some(ev) if ev.is_dropped() => (),\n                    _ => break ev,\n                }\n            }\n        } else {\n            self.next_event_priv(is_blocking, blocking_timeout)\n        };\n\n        if self.update_state {\n            if let Some(ref ev) = ev {\n                self.update(ev);\n            }\n        }\n\n        ev\n    }\n\n    /// Returns next pending event.\n    fn next_event_priv(\n        &mut self,\n        is_blocking: bool,\n        blocking_timeout: Option<Duration>,\n    ) -> Option<Event> {\n        if let Ok(msg) = self.rx.try_recv() {\n            return match msg {\n                FfMessage::EffectCompleted { event } => Some(event),\n            }\n        }\n        if let Some(ev) = self.events.pop_front() {\n            Some(ev)\n        } else {\n            let event = if is_blocking {\n                self.inner.next_event_blocking(blocking_timeout)\n            } else {\n                self.inner.next_event()\n            };\n\n            match event {\n                Some(RawEvent {\n                    id,\n                    event: event_type,\n                    time,\n                    ..\n                }) => {\n                    trace!(\"Original event: {:?}\", event);\n                    let id = GamepadId(id);\n\n                    let event = match event_type {\n                        RawEventType::ButtonPressed(nec) => {\n                            let nec = Code(nec);\n                            match self.gamepad(id).axis_or_btn_name(nec) {\n                                Some(AxisOrBtn::Btn(b)) => {\n                                    self.events.push_back(Event {\n                                        id,\n                                        time,\n                                        event: EventType::ButtonChanged(b, 1.0, nec),\n                                    });\n\n                                    EventType::ButtonPressed(b, nec)\n                                }\n                                Some(AxisOrBtn::Axis(a)) => EventType::AxisChanged(a, 1.0, nec),\n                                None => {\n                                    self.events.push_back(Event {\n                                        id,\n                                        time,\n                                        event: EventType::ButtonChanged(Button::Unknown, 1.0, nec),\n                                    });\n\n                                    EventType::ButtonPressed(Button::Unknown, nec)\n                                }\n                            }\n                        }\n                        RawEventType::ButtonReleased(nec) => {\n                            let nec = Code(nec);\n                            match self.gamepad(id).axis_or_btn_name(nec) {\n                                Some(AxisOrBtn::Btn(b)) => {\n                                    self.events.push_back(Event {\n                                        id,\n                                        time,\n                                        event: EventType::ButtonChanged(b, 0.0, nec),\n                                    });\n\n                                    EventType::ButtonReleased(b, nec)\n                                }\n                                Some(AxisOrBtn::Axis(a)) => EventType::AxisChanged(a, 0.0, nec),\n                                None => {\n                                    self.events.push_back(Event {\n                                        id,\n                                        time,\n                                        event: EventType::ButtonChanged(Button::Unknown, 0.0, nec),\n                                    });\n\n                                    EventType::ButtonReleased(Button::Unknown, nec)\n                                }\n                            }\n                        }\n                        RawEventType::AxisValueChanged(val, nec) => {\n                            // Let's trust at least our backend code\n                            let axis_info = *self.gamepad(id).inner.axis_info(nec).unwrap();\n                            let nec = Code(nec);\n\n                            match self.gamepad(id).axis_or_btn_name(nec) {\n                                Some(AxisOrBtn::Btn(b)) => {\n                                    let val = btn_value(&axis_info, val);\n\n                                    if val >= self.axis_to_btn_pressed\n                                        && !self.gamepad(id).state().is_pressed(nec)\n                                    {\n                                        self.events.push_back(Event {\n                                            id,\n                                            time,\n                                            event: EventType::ButtonChanged(b, val, nec),\n                                        });\n\n                                        EventType::ButtonPressed(b, nec)\n                                    } else if val <= self.axis_to_btn_released\n                                        && self.gamepad(id).state().is_pressed(nec)\n                                    {\n                                        self.events.push_back(Event {\n                                            id,\n                                            time,\n                                            event: EventType::ButtonChanged(b, val, nec),\n                                        });\n\n                                        EventType::ButtonReleased(b, nec)\n                                    } else {\n                                        EventType::ButtonChanged(b, val, nec)\n                                    }\n                                }\n                                Some(AxisOrBtn::Axis(a)) => {\n                                    EventType::AxisChanged(a, axis_value(&axis_info, val, a), nec)\n                                }\n                                None => EventType::AxisChanged(\n                                    Axis::Unknown,\n                                    axis_value(&axis_info, val, Axis::Unknown),\n                                    nec,\n                                ),\n                            }\n                        }\n                        RawEventType::Connected => {\n                            match id.0.cmp(&self.gamepads_data.len()) {\n                                Ordering::Equal => {\n                                    self.gamepads_data.push(GamepadData::new(\n                                        id,\n                                        self.tx.clone(),\n                                        self.inner.gamepad(id.0).unwrap(),\n                                        &self.mappings,\n                                    ));\n                                }\n                                Ordering::Less => {\n                                    self.gamepads_data[id.0] = GamepadData::new(\n                                        id,\n                                        self.tx.clone(),\n                                        self.inner.gamepad(id.0).unwrap(),\n                                        &self.mappings,\n                                    );\n                                }\n                                Ordering::Greater => {\n                                    error!(\n                                        \"Platform implementation error: got Connected event with \\\n                                         id {}, when expected id {}\",\n                                        id.0,\n                                        self.gamepads_data.len()\n                                    );\n                                }\n                            }\n\n                            EventType::Connected\n                        }\n                        RawEventType::Disconnected => {\n                            let _ = self.tx.send(Message::Close { id: id.0 });\n\n                            EventType::Disconnected\n                        }\n                        _ => {\n                            unimplemented!()\n                        }\n                    };\n\n                    Some(Event { id, event, time })\n                }\n                None => None,\n            }\n        }\n    }\n\n    /// Updates internal state according to `event`.\n    ///\n    /// Please note, that it's not necessary to call this function unless you modify events by using\n    /// additional filters and disabled automatic updates when creating `Gilrs`.\n    pub fn update(&mut self, event: &Event) {\n        use crate::EventType::*;\n\n        let counter = self.counter;\n\n        let data = match self.gamepads_data.get_mut(event.id.0) {\n            Some(d) => d,\n            None => return,\n        };\n\n        match event.event {\n            ButtonPressed(_, nec) => {\n                data.state.set_btn_pressed(nec, true, counter, event.time);\n            }\n            ButtonReleased(_, nec) => {\n                data.state.set_btn_pressed(nec, false, counter, event.time);\n            }\n            ButtonRepeated(_, nec) => {\n                data.state.set_btn_repeating(nec, counter, event.time);\n            }\n            ButtonChanged(_, value, nec) => {\n                data.state.set_btn_value(nec, value, counter, event.time);\n            }\n            AxisChanged(_, value, nec) => {\n                data.state\n                    .update_axis(nec, AxisData::new(value, counter, event.time));\n            }\n            Disconnected | Connected | Dropped | ForceFeedbackEffectCompleted => (),\n        }\n    }\n\n    /// Increases internal counter by one. Counter data is stored with state and can be used to\n    /// determine when last event happened. You probably want to use this function in your update\n    /// loop after processing events.\n    pub fn inc(&mut self) {\n        // Counter is 62bit. See `ButtonData`.\n        if self.counter == 0x3FFF_FFFF_FFFF_FFFF {\n            self.counter = 0;\n        } else {\n            self.counter += 1;\n        }\n    }\n\n    /// Returns counter. Counter data is stored with state and can be used to determine when last\n    /// event happened.\n    pub fn counter(&self) -> u64 {\n        self.counter\n    }\n\n    /// Sets counter to 0.\n    pub fn reset_counter(&mut self) {\n        self.counter = 0;\n    }\n\n    fn finish_gamepads_creation(&mut self) {\n        let tx = self.tx.clone();\n        for id in 0..self.inner.last_gamepad_hint() {\n            let gamepad = self.inner.gamepad(id).unwrap();\n            self.gamepads_data.push(GamepadData::new(\n                GamepadId(id),\n                tx.clone(),\n                gamepad,\n                &self.mappings,\n            ))\n        }\n    }\n\n    /// Returns handle to gamepad with given ID. Unlike `connected_gamepad()`, this function will\n    /// also return handle to gamepad that is currently disconnected.\n    ///\n    /// ```\n    /// # let mut gilrs = gilrs::Gilrs::new().unwrap();\n    /// use gilrs::{Button, EventType};\n    ///\n    /// loop {\n    ///     while let Some(ev) = gilrs.next_event() {\n    ///         // unwrap() should never panic because we use id from event\n    ///         let is_up_pressed = gilrs.gamepad(ev.id).is_pressed(Button::DPadUp);\n    ///\n    ///         match ev.event {\n    ///             EventType::ButtonPressed(Button::South, _) if is_up_pressed => {\n    ///                 // do something…\n    ///             }\n    ///             _ => (),\n    ///         }\n    ///     }\n    ///     # break;\n    /// }\n    /// ```\n    pub fn gamepad(&self, id: GamepadId) -> Gamepad {\n        Gamepad {\n            inner: self.inner.gamepad(id.0).unwrap(),\n            data: &self.gamepads_data[id.0],\n        }\n    }\n\n    /// Returns a reference to connected gamepad or `None`.\n    pub fn connected_gamepad(&self, id: GamepadId) -> Option<Gamepad<'_>> {\n        // Make sure that it will not panic even with invalid GamepadId, so ConnectedGamepadIterator\n        // will always work.\n        if let Some(data) = self.gamepads_data.get(id.0) {\n            let inner = self.inner.gamepad(id.0)?;\n\n            if inner.is_connected() {\n                Some(Gamepad { inner, data })\n            } else {\n                None\n            }\n        } else {\n            None\n        }\n    }\n\n    /// Returns iterator over all connected gamepads and their ids.\n    ///\n    /// ```\n    /// # let gilrs = gilrs::Gilrs::new().unwrap();\n    /// for (id, gamepad) in gilrs.gamepads() {\n    ///     assert!(gamepad.is_connected());\n    ///     println!(\"Gamepad with id {} and name {} is connected\",\n    ///              id, gamepad.name());\n    /// }\n    /// ```\n    pub fn gamepads(&self) -> ConnectedGamepadsIterator<'_> {\n        ConnectedGamepadsIterator(self, 0)\n    }\n\n    /// Adds `ev` at the end of internal event queue. It can later be retrieved with `next_event()`.\n    pub fn insert_event(&mut self, ev: Event) {\n        self.events.push_back(ev);\n    }\n\n    pub(crate) fn ff_sender(&self) -> &Sender<Message> {\n        &self.tx\n    }\n\n    /// Sets gamepad's mapping and returns SDL2 representation of them. Returned mappings may not be\n    /// compatible with SDL2 - if it is important, use\n    /// [`set_mapping_strict()`](#method.set_mapping_strict).\n    ///\n    /// The `name` argument can be a string slice with custom gamepad name or `None`. If `None`,\n    /// gamepad name reported by driver will be used.\n    ///\n    /// # Errors\n    ///\n    /// This function return error if `name` contains comma, `mapping` have axis and button entry\n    /// for same element (for example `Axis::LetfTrigger` and `Button::LeftTrigger`) or gamepad does\n    /// not have any element with `EvCode` used in mapping. `Button::Unknown` and\n    /// `Axis::Unknown` are not allowd as keys to `mapping` – in this case,\n    /// `MappingError::UnknownElement` is returned.\n    ///\n    /// Error is also returned if this function is not implemented or gamepad is not connected.\n    ///\n    /// # Example\n    ///\n    /// ```\n    /// use gilrs::{Mapping, Button};\n    ///\n    /// # let mut gilrs = gilrs::Gilrs::new().unwrap();\n    /// let mut data = Mapping::new();\n    /// // …\n    ///\n    /// // or `match gilrs.set_mapping(0, &data, None) {`\n    /// match gilrs.set_mapping(0, &data, \"Custom name\") {\n    ///     Ok(sdl) => println!(\"SDL2 mapping: {}\", sdl),\n    ///     Err(e) => println!(\"Failed to set mapping: {}\", e),\n    /// };\n    /// ```\n    ///\n    /// See also `examples/mapping.rs`.\n    pub fn set_mapping<'b, O: Into<Option<&'b str>>>(\n        &mut self,\n        gamepad_id: usize,\n        mapping: &MappingData,\n        name: O,\n    ) -> Result<String, MappingError> {\n        if let Some(gamepad) = self.inner.gamepad(gamepad_id) {\n            if !gamepad.is_connected() {\n                return Err(MappingError::NotConnected);\n            }\n\n            let name = match name.into() {\n                Some(s) => s,\n                None => gamepad.name(),\n            };\n\n            let (mapping, s) = Mapping::from_data(\n                mapping,\n                gamepad.buttons(),\n                gamepad.axes(),\n                name,\n                Uuid::from_bytes(gamepad.uuid()),\n            )?;\n\n            // We checked if gamepad is connected, so it should never panic\n            let data = &mut self.gamepads_data[gamepad_id];\n            data.mapping = mapping;\n\n            Ok(s)\n        } else {\n            Err(MappingError::NotConnected)\n        }\n    }\n\n    /// Similar to [`set_mapping()`](#method.set_mapping) but returned string should be compatible\n    /// with SDL2.\n    ///\n    /// # Errors\n    ///\n    /// Returns `MappingError::NotSdl2Compatible` if `mapping` have an entry for `Button::{C, Z}`\n    /// or `Axis::{LeftZ, RightZ}`.\n    pub fn set_mapping_strict<'b, O: Into<Option<&'b str>>>(\n        &mut self,\n        gamepad_id: usize,\n        mapping: &MappingData,\n        name: O,\n    ) -> Result<String, MappingError> {\n        if mapping.button(Button::C).is_some()\n            || mapping.button(Button::Z).is_some()\n            || mapping.axis(Axis::LeftZ).is_some()\n            || mapping.axis(Axis::RightZ).is_some()\n        {\n            Err(MappingError::NotSdl2Compatible)\n        } else {\n            self.set_mapping(gamepad_id, mapping, name)\n        }\n    }\n\n    pub(crate) fn next_ff_id(&mut self) -> usize {\n        // TODO: reuse free ids\n        let id = self.next_id;\n        self.next_id = match self.next_id.checked_add(1) {\n            Some(x) => x,\n            None => panic!(\"Failed to assign ID to new effect\"),\n        };\n        id\n    }\n}\n\n/// Allow to create `Gilrs ` with customized behaviour.\npub struct GilrsBuilder {\n    mappings: MappingDb,\n    default_filters: bool,\n    axis_to_btn_pressed: f32,\n    axis_to_btn_released: f32,\n    update_state: bool,\n    env_mappings: bool,\n    included_mappings: bool,\n}\n\nimpl GilrsBuilder {\n    /// Create builder with default settings. Use `build()` to create `Gilrs`.\n    pub fn new() -> Self {\n        GilrsBuilder {\n            mappings: MappingDb::new(),\n            default_filters: true,\n            axis_to_btn_pressed: 0.75,\n            axis_to_btn_released: 0.65,\n            update_state: true,\n            env_mappings: true,\n            included_mappings: true,\n        }\n    }\n\n    /// If `true`, use [`axis_dpad_to_button`](ev/filter/fn.axis_dpad_to_button.html),\n    /// [`Jitter`](ev/filter/struct.Jitter.html) and [`deadzone`](ev/filter/fn.deadzone.html)\n    /// filters with default parameters. Defaults to `true`.\n    pub fn with_default_filters(mut self, default_filters: bool) -> Self {\n        self.default_filters = default_filters;\n\n        self\n    }\n\n    /// Adds SDL mappings.\n    pub fn add_mappings(mut self, mappings: &str) -> Self {\n        self.mappings.insert(mappings);\n\n        self\n    }\n\n    /// If true, will add SDL mappings from `SDL_GAMECONTROLLERCONFIG` environment variable.\n    /// Defaults to true.\n    pub fn add_env_mappings(mut self, env_mappings: bool) -> Self {\n        self.env_mappings = env_mappings;\n\n        self\n    }\n\n    /// If true, will add SDL mappings included from\n    /// https://github.com/gabomdq/SDL_GameControllerDB. Defaults to true.\n    pub fn add_included_mappings(mut self, included_mappings: bool) -> Self {\n        self.included_mappings = included_mappings;\n\n        self\n    }\n\n    /// Sets values on which `ButtonPressed` and `ButtonReleased` events will be emitted. `build()`\n    /// will return error if `pressed ≤ released` or if one of values is outside [0.0, 1.0].\n    ///\n    /// Defaults to 0.75 for `pressed` and 0.65 for `released`.\n    pub fn set_axis_to_btn(mut self, pressed: f32, released: f32) -> Self {\n        self.axis_to_btn_pressed = pressed;\n        self.axis_to_btn_released = released;\n\n        self\n    }\n\n    /// Disable or enable automatic state updates. You should use this if you use custom filters;\n    /// in this case you have to update state manually anyway.\n    pub fn set_update_state(mut self, enabled: bool) -> Self {\n        self.update_state = enabled;\n\n        self\n    }\n\n    /// Creates `Gilrs`.\n    pub fn build(mut self) -> Result<Gilrs, Error> {\n        if self.included_mappings {\n            self.mappings.add_included_mappings();\n        }\n\n        if self.env_mappings {\n            self.mappings.add_env_mappings();\n        }\n\n        debug!(\"Loaded {} mappings.\", self.mappings.len());\n\n        if self.axis_to_btn_pressed <= self.axis_to_btn_released\n            || self.axis_to_btn_pressed < 0.0\n            || self.axis_to_btn_pressed > 1.0\n            || self.axis_to_btn_released < 0.0\n            || self.axis_to_btn_released > 1.0\n        {\n            return Err(Error::InvalidAxisToBtn);\n        }\n\n        let mut is_dummy = false;\n        let inner = match gilrs_core::Gilrs::new() {\n            Ok(g) => g,\n            Err(PlatformError::NotImplemented(g)) => {\n                is_dummy = true;\n\n                g\n            }\n            Err(PlatformError::Other(e)) => return Err(Error::Other(e)),\n            Err(_) => unimplemented!(),\n        };\n\n        let (tx, rx) = server::init();\n\n        let mut gilrs = Gilrs {\n            inner,\n            next_id: 0,\n            tx,\n            rx,\n            counter: 0,\n            mappings: self.mappings,\n            default_filters: self.default_filters,\n            events: VecDeque::new(),\n            axis_to_btn_pressed: self.axis_to_btn_pressed,\n            axis_to_btn_released: self.axis_to_btn_released,\n            update_state: self.update_state,\n            gamepads_data: Vec::new(),\n        };\n        gilrs.finish_gamepads_creation();\n\n        if is_dummy {\n            Err(Error::NotImplemented(gilrs))\n        } else {\n            Ok(gilrs)\n        }\n    }\n}\n\nimpl Default for GilrsBuilder {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\n/// Iterator over all connected gamepads.\npub struct ConnectedGamepadsIterator<'a>(&'a Gilrs, usize);\n\nimpl<'a> Iterator for ConnectedGamepadsIterator<'a> {\n    type Item = (GamepadId, Gamepad<'a>);\n\n    fn next(&mut self) -> Option<(GamepadId, Gamepad<'a>)> {\n        loop {\n            if self.1 == self.0.inner.last_gamepad_hint() {\n                return None;\n            }\n\n            if let Some(gp) = self.0.connected_gamepad(GamepadId(self.1)) {\n                let idx = self.1;\n                self.1 += 1;\n                return Some((GamepadId(idx), gp));\n            }\n\n            self.1 += 1;\n        }\n    }\n}\n\n/// Represents handle to game controller.\n///\n/// Using this struct you can access cached gamepad state, information about gamepad such as name\n/// or UUID and manage force feedback effects.\n#[derive(Debug, Copy, Clone)]\npub struct Gamepad<'a> {\n    data: &'a GamepadData,\n    inner: &'a gilrs_core::Gamepad,\n}\n\nimpl Gamepad<'_> {\n    /// Returns the mapping name if it exists otherwise returns the os provided name.\n    pub fn name(&self) -> &str {\n        if let Some(map_name) = self.map_name() {\n            map_name\n        } else {\n            self.os_name()\n        }\n    }\n\n    /// if `mapping_source()` is `SdlMappings` returns the name of the mapping used by the gamepad.\n    /// Otherwise returns `None`.\n    pub fn map_name(&self) -> Option<&str> {\n        self.data.map_name()\n    }\n\n    /// Returns the name of the gamepad supplied by the OS.\n    pub fn os_name(&self) -> &str {\n        self.inner.name()\n    }\n\n    /// Returns gamepad's UUID.\n    ///\n    /// It is recommended to process with the [UUID crate](https://crates.io/crates/uuid).\n    /// Use `Uuid::from_bytes` method to create a `Uuid` from the returned bytes.\n    pub fn uuid(&self) -> [u8; 16] {\n        self.inner.uuid()\n    }\n\n    /// Returns the vendor ID, as assigned by the USB-IF, when available.\n    pub fn vendor_id(&self) -> Option<u16> {\n        self.inner.vendor_id()\n    }\n\n    /// Returns the product ID, as assigned by the vendor, when available.\n    pub fn product_id(&self) -> Option<u16> {\n        self.inner.product_id()\n    }\n\n    /// Returns cached gamepad state.\n    pub fn state(&self) -> &GamepadState {\n        &self.data.state\n    }\n\n    /// Returns true if gamepad is connected.\n    pub fn is_connected(&self) -> bool {\n        self.inner.is_connected()\n    }\n\n    /// Examines cached gamepad state to check if given button is pressed. Panics if `btn` is\n    /// `Unknown`.\n    ///\n    /// If you know `Code` of the element that you want to examine, it's recommended to use methods\n    /// directly on `State`, because this version have to check which `Code` is mapped to element of\n    /// gamepad.\n    pub fn is_pressed(&self, btn: Button) -> bool {\n        self.data.is_pressed(btn)\n    }\n\n    /// Examines cached gamepad state to check axis's value. Panics if `axis` is `Unknown`.\n    ///\n    /// If you know `Code` of the element that you want to examine, it's recommended to use methods\n    /// directly on `State`, because this version have to check which `Code` is mapped to element of\n    /// gamepad.\n    pub fn value(&self, axis: Axis) -> f32 {\n        self.data.value(axis)\n    }\n\n    /// Returns button state and when it changed.\n    ///\n    /// If you know `Code` of the element that you want to examine, it's recommended to use methods\n    /// directly on `State`, because this version have to check which `Code` is mapped to element of\n    /// gamepad.\n    pub fn button_data(&self, btn: Button) -> Option<&ButtonData> {\n        self.data.button_data(btn)\n    }\n\n    /// Returns axis state and when it changed.\n    ///\n    /// If you know `Code` of the element that you want to examine, it's recommended to use methods\n    /// directly on `State`, because this version have to check which `Code` is mapped to element of\n    /// gamepad.\n    pub fn axis_data(&self, axis: Axis) -> Option<&AxisData> {\n        self.data.axis_data(axis)\n    }\n\n    /// Returns device's power supply state. See [`PowerInfo`](enum.PowerInfo.html) for details.\n    pub fn power_info(&self) -> PowerInfo {\n        self.inner.power_info()\n    }\n\n    /// Returns source of gamepad mapping. Can be used to filter gamepads which do not provide\n    /// unified controller layout.\n    ///\n    /// ```\n    /// use gilrs::MappingSource;\n    /// # let mut gilrs = gilrs::Gilrs::new().unwrap();\n    ///\n    /// for (_, gamepad) in gilrs.gamepads().filter(\n    ///     |gp| gp.1.mapping_source() != MappingSource::None)\n    /// {\n    ///     println!(\"{} is ready to use!\", gamepad.name());\n    /// }\n    /// ```\n    pub fn mapping_source(&self) -> MappingSource {\n        if self.data.mapping.is_default() {\n            // TODO: check if it's Driver or None\n            MappingSource::Driver\n        } else {\n            MappingSource::SdlMappings\n        }\n    }\n\n    /// Returns true if force feedback is supported by device.\n    pub fn is_ff_supported(&self) -> bool {\n        self.inner.is_ff_supported()\n    }\n\n    /// Change gamepad position used by force feedback effects.\n    pub fn set_listener_position<Vec3: Into<[f32; 3]>>(\n        &self,\n        position: Vec3,\n    ) -> Result<(), FfError> {\n        if !self.is_connected() {\n            Err(FfError::Disconnected(self.id()))\n        } else if !self.is_ff_supported() {\n            Err(FfError::FfNotSupported(self.id()))\n        } else {\n            self.data.tx.send(Message::SetListenerPosition {\n                id: self.data.id.0,\n                position: position.into(),\n            })?;\n            Ok(())\n        }\n    }\n\n    /// Returns `AxisOrBtn` mapped to `Code`.\n    pub fn axis_or_btn_name(&self, ec: Code) -> Option<AxisOrBtn> {\n        self.data.axis_or_btn_name(ec)\n    }\n\n    /// Returns `Code` associated with `btn`.\n    pub fn button_code(&self, btn: Button) -> Option<Code> {\n        self.data.button_code(btn)\n    }\n\n    /// Returns `Code` associated with `axis`.\n    pub fn axis_code(&self, axis: Axis) -> Option<Code> {\n        self.data.axis_code(axis)\n    }\n\n    /// Returns area in which axis events should be ignored.\n    pub fn deadzone(&self, axis: Code) -> Option<f32> {\n        self.inner.axis_info(axis.0).map(|i| {\n            let range = i.max as f32 - i.min as f32;\n\n            if range == 0.0 {\n                0.0\n            } else {\n                i.deadzone\n                    .map(|d| d as f32 / range * 2.0)\n                    .unwrap_or(DEFAULT_DEADZONE)\n            }\n        })\n    }\n\n    /// Returns ID of gamepad.\n    pub fn id(&self) -> GamepadId {\n        self.data.id\n    }\n\n    pub(crate) fn mapping(&self) -> &Mapping {\n        &self.data.mapping\n    }\n}\n\n#[derive(Debug)]\npub(crate) struct GamepadData {\n    state: GamepadState,\n    mapping: Mapping,\n    tx: Sender<Message>,\n    id: GamepadId,\n    // Flags used by the deadzone filter.\n    pub(crate) have_sent_nonzero_for_axis: [bool; 6],\n}\n\nimpl GamepadData {\n    fn new(\n        id: GamepadId,\n        tx: Sender<Message>,\n        gamepad: &gilrs_core::Gamepad,\n        db: &MappingDb,\n    ) -> Self {\n        let uuid = Uuid::from_bytes(gamepad.uuid());\n        let mapping = db\n            .get(uuid)\n            .map(\n                |s| match Mapping::parse_sdl_mapping(s, gamepad.buttons(), gamepad.axes()) {\n                    Ok(result) => result,\n                    Err(e) => {\n                        warn!(\n                            \"Unable to parse SDL mapping for UUID {uuid}\\n\\t{e:?}\\n\\tDefault mapping \\\n                             will be used.\",\n                        );\n                        Mapping::default(gamepad)\n                    }\n                },\n            )\n            .unwrap_or_else(|| {\n                warn!(\"No mapping found for UUID {uuid}\\n\\tDefault mapping will be used.\");\n                Mapping::default(gamepad)\n            });\n\n        if gamepad.is_ff_supported() && gamepad.is_connected() {\n            if let Some(device) = gamepad.ff_device() {\n                let _ = tx.send(Message::Open { id: id.0, device });\n            }\n        }\n\n        GamepadData {\n            state: GamepadState::new(),\n            mapping,\n            tx,\n            id,\n            have_sent_nonzero_for_axis: Default::default(),\n        }\n    }\n\n    /// if `mapping_source()` is `SdlMappings` returns the name of the mapping used by the gamepad.\n    /// Otherwise returns `None`.\n    ///\n    /// Warning: Mappings are set after event `Connected` is processed, therefore this function will\n    /// always return `None` before first calls to `Gilrs::next_event()`.\n    pub fn map_name(&self) -> Option<&str> {\n        if self.mapping.is_default() {\n            None\n        } else {\n            Some(self.mapping.name())\n        }\n    }\n\n    /// Examines cached gamepad state to check if the given button is pressed. Panics if `btn` is\n    /// `Unknown`.\n    ///\n    /// If you know `Code` of the element that you want to examine, it's recommended to use methods\n    /// directly on `State`, because this version has to check which `Code` is mapped to element of\n    /// gamepad.\n    pub fn is_pressed(&self, btn: Button) -> bool {\n        assert_ne!(btn, Button::Unknown);\n\n        self.button_code(btn)\n            .or_else(|| btn.to_nec())\n            .map(|nec| self.state.is_pressed(nec))\n            .unwrap_or(false)\n    }\n\n    /// Examines cached gamepad state to check axis's value. Panics if `axis` is `Unknown`.\n    ///\n    /// If you know `Code` of the element that you want to examine, it's recommended to use methods\n    /// directly on `State`, because this version has to check which `Code` is mapped to element of\n    /// gamepad.\n    pub fn value(&self, axis: Axis) -> f32 {\n        assert_ne!(axis, Axis::Unknown);\n\n        self.axis_code(axis)\n            .map(|nec| self.state.value(nec))\n            .unwrap_or(0.0)\n    }\n\n    /// Returns button state and when it changed.\n    ///\n    /// If you know `Code` of the element that you want to examine, it's recommended to use methods\n    /// directly on `State`, because this version has to check which `Code` is mapped to element of\n    /// gamepad.\n    pub fn button_data(&self, btn: Button) -> Option<&ButtonData> {\n        self.button_code(btn)\n            .and_then(|nec| self.state.button_data(nec))\n    }\n\n    /// Returns axis state and when it changed.\n    ///\n    /// If you know `Code` of the element that you want to examine, it's recommended to use methods\n    /// directly on `State`, because this version has to check which `Code` is mapped to element of\n    /// gamepad.\n    pub fn axis_data(&self, axis: Axis) -> Option<&AxisData> {\n        self.axis_code(axis)\n            .and_then(|nec| self.state.axis_data(nec))\n    }\n\n    /// Returns `AxisOrBtn` mapped to `Code`.\n    pub fn axis_or_btn_name(&self, ec: Code) -> Option<AxisOrBtn> {\n        self.mapping.map(&ec.0)\n    }\n\n    /// Returns `Code` associated with `btn`.\n    pub fn button_code(&self, btn: Button) -> Option<Code> {\n        self.mapping.map_rev(&AxisOrBtn::Btn(btn)).map(Code)\n    }\n\n    /// Returns `Code` associated with `axis`.\n    pub fn axis_code(&self, axis: Axis) -> Option<Code> {\n        self.mapping.map_rev(&AxisOrBtn::Axis(axis)).map(Code)\n    }\n}\n\n/// Source of gamepad mappings.\n#[derive(Copy, Clone, Debug, PartialEq, Eq)]\npub enum MappingSource {\n    /// Gamepad uses SDL mappings.\n    SdlMappings,\n    /// Gamepad does not use any mappings but driver should provide unified controller layout.\n    Driver,\n    /// Gamepad does not use any mappings and most gamepad events will probably be `Button::Unknown`\n    /// or `Axis::Unknown`\n    None,\n}\n\n/// Gamepad ID.\n///\n/// It's not possible to create an instance of this type directly, but you can obtain one from a Gamepad\n/// handle or any event. ID is valid for the entire lifetime of the `Gilrs` context.\n#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]\n#[cfg_attr(feature = \"serde-serialize\", derive(Serialize, Deserialize))]\npub struct GamepadId(pub(crate) usize);\n\nimpl From<GamepadId> for usize {\n    fn from(x: GamepadId) -> usize {\n        x.0\n    }\n}\n\nimpl Display for GamepadId {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        self.0.fmt(f)\n    }\n}\n\nfn axis_value(info: &AxisInfo, val: i32, axis: Axis) -> f32 {\n    let mut range = info.max as f32 - info.min as f32;\n    let mut val = val as f32 - info.min as f32;\n\n    if let Some(i_range) = info.max.checked_sub(info.min) {\n        // Only consider adjusting range & val if calculating the range doesn't cause overflow.  If\n        // the range is so large overflow occurs, adjusting values by 1.0 would be insignificant.\n        if i_range % 2 == 1 {\n            // Add one to range and val, so the value is centered (like 127/255) will be mapped 0.0\n            range += 1.0;\n            val += 1.0;\n        }\n    }\n\n    val = val / range * 2.0 - 1.0;\n\n    if gilrs_core::IS_Y_AXIS_REVERSED\n        && (axis == Axis::LeftStickY || axis == Axis::RightStickY || axis == Axis::DPadY)\n        && val != 0.0\n    {\n        val = -val;\n    }\n\n    utils::clamp(val, -1.0, 1.0)\n}\n\nfn btn_value(info: &AxisInfo, val: i32) -> f32 {\n    let range = info.max as f32 - info.min as f32;\n    let mut val = val as f32 - info.min as f32;\n    val /= range;\n\n    utils::clamp(val, 0.0, 1.0)\n}\n\n/// Error type which can be returned when creating `Gilrs`.\n#[non_exhaustive]\n#[derive(Debug)]\npub enum Error {\n    /// Gilrs does not support the current platform, but you can use dummy context from this error if\n    /// gamepad input is not essential.\n    NotImplemented(Gilrs),\n    /// Either `pressed ≤ released` or one of values is outside [0.0, 1.0] range.\n    InvalidAxisToBtn,\n    /// Platform specific error.\n    Other(Box<dyn error::Error + Send + Sync + 'static>),\n}\n\nimpl Display for Error {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self {\n            Error::NotImplemented(_) => f.write_str(\"Gilrs does not support current platform.\"),\n            Error::InvalidAxisToBtn => f.write_str(\n                \"Either `pressed ≤ released` or one of values is outside [0.0, 1.0] range.\",\n            ),\n            Error::Other(ref e) => e.fmt(f),\n        }\n    }\n}\n\nimpl error::Error for Error {\n    fn source(&self) -> Option<&(dyn error::Error + 'static)> {\n        match self {\n            Error::Other(e) => Some(e.as_ref()),\n            _ => None,\n        }\n    }\n}\n\nconst _: () = {\n    const fn assert_send<T: Send>() {}\n\n    #[cfg(not(target_arch = \"wasm32\"))]\n    assert_send::<Gilrs>();\n};\n\n#[cfg(test)]\nmod tests {\n    use super::{axis_value, btn_value, Axis, AxisInfo};\n\n    #[test]\n    fn axis_value_documented_case() {\n        let info = AxisInfo {\n            min: 0,\n            max: 255,\n            deadzone: None,\n        };\n        let axis = Axis::LeftStickY;\n        assert_eq!(0., axis_value(&info, 127, axis));\n    }\n\n    #[test]\n    fn axis_value_overflow() {\n        let info = AxisInfo {\n            min: i32::MIN,\n            max: i32::MAX,\n            deadzone: None,\n        };\n        let axis = Axis::LeftStickY;\n\n        assert_eq!(0., axis_value(&info, -1, axis));\n        assert_eq!(0., axis_value(&info, 0, axis));\n        assert_eq!(0., axis_value(&info, 1, axis));\n\n        assert_eq!(1.0, axis_value(&info, i32::MIN, axis));\n        assert_eq!(-1.0, axis_value(&info, i32::MAX, axis));\n    }\n\n    #[test]\n    fn btn_value_overflow() {\n        let info = AxisInfo {\n            min: i32::MIN,\n            max: i32::MAX,\n            deadzone: None,\n        };\n\n        assert_eq!(0.5, btn_value(&info, -1));\n        assert_eq!(0.5, btn_value(&info, 0));\n        assert_eq!(0.5, btn_value(&info, 1));\n\n        assert_eq!(0.0, btn_value(&info, i32::MIN));\n        assert_eq!(1.0, btn_value(&info, i32::MAX));\n    }\n}\n"
  },
  {
    "path": "gilrs/src/lib.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\n\n//! GilRs - Game Input Library for Rust\n//! ===================================\n//!\n//! GilRs abstract platform specific APIs to provide unified interfaces for working with gamepads.\n//!\n//! Main features:\n//!\n//! - Unified gamepad layout—buttons and axes are represented by familiar names\n//! - Support for SDL2 mappings including `SDL_GAMECONTROLLERCONFIG` environment\n//!   variable which Steam uses\n//! - Hotplugging—GilRs will try to assign new IDs for new gamepads and reuse the same\n//!   ID for gamepads which reconnected\n//! - Force feedback (rumble)\n//! - Power information (is gamepad wired, current battery status)\n//!\n//! Example\n//! -------\n//!\n//! ```rust\n//! use gilrs::{Gilrs, Button, Event};\n//!\n//! let mut gilrs = Gilrs::new().unwrap();\n//!\n//! // Iterate over all connected gamepads\n//! for (_id, gamepad) in gilrs.gamepads() {\n//!     println!(\"{} is {:?}\", gamepad.name(), gamepad.power_info());\n//! }\n//!\n//! let mut active_gamepad = None;\n//!\n//! loop {\n//!     // Examine new events\n//!     while let Some(Event { id, event, time, .. }) = gilrs.next_event() {\n//!         println!(\"{:?} New event from {}: {:?}\", time, id, event);\n//!         active_gamepad = Some(id);\n//!     }\n//!\n//!     // You can also use cached gamepad state\n//!     if let Some(gamepad) = active_gamepad.map(|id| gilrs.gamepad(id)) {\n//!         if gamepad.is_pressed(Button::South) {\n//!             println!(\"Button South is pressed (XBox - A, PS - X)\");\n//!         }\n//!     }\n//!     # break;\n//! }\n//! ```\n//!\n//! Supported features\n//! ------------------\n//!\n//! |                  | Input | Hotplugging | Force feedback |\n//! |------------------|:-----:|:-----------:|:--------------:|\n//! | Linux/BSD (evdev)|   ✓   |      ✓      |        ✓       |\n//! | Windows          |   ✓   |      ✓      |        ✓       |\n//! | OS X             |   ✓   |      ✓      |        ✕       |\n//! | Wasm             |   ✓   |      ✓      |       n/a      |\n//! | Android          |   ✕   |      ✕      |        ✕       |\n//!\n//! Controller layout\n//! -----------------\n//!\n//! ![Controller layout](https://gilrs-project.gitlab.io/gilrs/img/controller.svg)\n//! [original image by nicefrog](http://opengameart.org/content/generic-gamepad-template)\n//!\n//! Mappings\n//! --------\n//!\n//! GilRs use SDL-compatible controller mappings to fix on Linux legacy drivers that doesn't follow\n//! [Linux Gamepad API](https://www.kernel.org/doc/Documentation/input/gamepad.txt) and to provide\n//! unified button layout for platforms that doesn't make any guarantees about it. The main source\n//! is [SDL_GameControllerDB](https://github.com/gabomdq/SDL_GameControllerDB), but library also\n//! support loading mappings from environment variable `SDL_GAMECONTROLLERCONFIG` (which Steam\n//! use).\n//!\n//! Cargo features\n//! --------------\n//!\n//! - `serde-serialize` - enable deriving of serde's `Serialize` and `Deserialize` for\n//!   various types.\n//! - `wgi` - use Windows Gaming Input on Windows (enabled by default).\n//! - `xinput` - use XInput on Windows.\n//!\n//! Platform specific notes\n//! ======================\n//!\n//! Linux/BSD (evdev)\n//! -----\n//!\n//! With evdev, GilRs read (and write, in case of force feedback) directly from appropriate\n//! `/dev/input/event*` file. This mean that user have to have read and write access to this file.\n//! On most distros it shouldn't be a problem, but if it is, you will have to create udev rule.\n//! On FreeBSD generic HID gamepads use hgame(4) and special use Linux driver via `webcamd`.\n//!\n//! To build GilRs, you will need pkg-config and libudev .pc file. On some distributions this file\n//! is packaged in separate archive (e.g., `libudev-dev` in Debian, `libudev-devd` in FreeBSD).\n//!\n//! Windows\n//! -----\n//!\n//! Windows defaults to using Windows Gaming Input instead of XInput. If you need to use XInput you\n//! can disable the `wgi` feature (it's enabled by default) and enable the `xinput` feature.\n//!\n//! Windows Gaming Input requires an in focus window to be associated with the process to receive\n//! events. You can still switch back to using xInput by turning off default features and enabling\n//! the xinput feature.\n//!\n//! Note: Some (Older?) devices may still report inputs without a window but this is not the case\n//! for all devices so if you are writing a terminal based game, use the xinput feature instead.\n//!\n//! Wasm\n//! -----\n//!\n//! Wasm implementation uses stdweb, or wasm-bindgen with the wasm-bindgen feature.\n//! For stdweb, you will need [cargo-web](https://github.com/koute/cargo-web) to build gilrs for\n//! wasm32-unknown-unknown. For wasm-bindgen, you will need the wasm-bindgen cli or a tool like\n//! [wasm-pack](https://rustwasm.github.io/wasm-pack/installer/).\n//! Unlike other platforms, events are only generated when you call `Gilrs::next_event()`.\n\n#[macro_use]\nextern crate log;\n\nmod constants;\nmod gamepad;\nmod mapping;\nmod utils;\n\npub mod ev;\npub mod ff;\n\npub use crate::ev::filter::Filter;\npub use crate::ev::{Axis, Button, Event, EventType};\npub use crate::gamepad::{\n    ConnectedGamepadsIterator, Error, Gamepad, GamepadId, Gilrs, GilrsBuilder, MappingSource,\n    PowerInfo,\n};\npub use crate::mapping::{MappingData as Mapping, MappingError};\n"
  },
  {
    "path": "gilrs/src/mapping/mod.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\n#![cfg_attr(target_os = \"windows\", allow(dead_code))]\n\nmod parser;\n\nuse crate::ev::{self, Axis, AxisOrBtn, Button};\nuse crate::utils::PATH_SEPARATOR;\nuse gilrs_core::native_ev_codes as nec;\nuse gilrs_core::EvCode;\n\nuse std::collections::HashMap;\nuse std::env;\nuse std::error::Error;\nuse std::fmt::{Display, Formatter, Result as FmtResult, Write as _};\n\nuse fnv::FnvHashMap;\nuse uuid::Uuid;\nuse vec_map::VecMap;\n\nuse self::parser::{Error as ParserError, ErrorKind as ParserErrorKind, Parser, Token};\n\n/// Platform name used by SDL mappings\n#[cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\"))]\nconst SDL_PLATFORM_NAME: &str = \"Linux\";\n#[cfg(target_os = \"macos\")]\nconst SDL_PLATFORM_NAME: &str = \"Mac OS X\";\n#[cfg(target_os = \"windows\")]\nconst SDL_PLATFORM_NAME: &str = \"Windows\";\n#[cfg(all(\n    not(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\")),\n    not(target_os = \"macos\"),\n    not(target_os = \"windows\")\n))]\nconst SDL_PLATFORM_NAME: &str = \"Unknown\";\n\n#[derive(Debug)]\n#[cfg_attr(test, derive(PartialEq))]\n/// Store mappings from one `EvCode` (`u16`) to another.\n///\n/// This struct is internal, `MappingData` is exported in public interface as `Mapping`.\npub struct Mapping {\n    mappings: FnvHashMap<EvCode, AxisOrBtn>,\n    name: String,\n    default: bool,\n    hats_mapped: u8,\n}\n\nimpl Mapping {\n    pub fn new() -> Self {\n        Mapping {\n            mappings: FnvHashMap::default(),\n            name: String::new(),\n            default: false,\n            hats_mapped: 0,\n        }\n    }\n\n    pub fn default(gamepad: &gilrs_core::Gamepad) -> Self {\n        use self::Axis as Ax;\n        use self::AxisOrBtn::*;\n\n        macro_rules! fnv_map {\n            ( $( $key:expr => $elem:expr ),* ) => {\n                {\n                    let mut map = FnvHashMap::default();\n                    $(\n                        map.insert($key, $elem);\n                    )*\n\n                    map\n                }\n            };\n        }\n\n        let mut mappings = fnv_map![\n            nec::BTN_SOUTH => Btn(Button::South),\n            nec::BTN_EAST => Btn(Button::East),\n            nec::BTN_C => Btn(Button::C),\n            nec::BTN_NORTH => Btn(Button::North),\n            nec::BTN_WEST => Btn(Button::West),\n            nec::BTN_Z => Btn(Button::Z),\n            nec::BTN_LT => Btn(Button::LeftTrigger),\n            nec::BTN_RT => Btn(Button::RightTrigger),\n            nec::BTN_LT2 => Btn(Button::LeftTrigger2),\n            nec::BTN_RT2 => Btn(Button::RightTrigger2),\n            nec::BTN_SELECT => Btn(Button::Select),\n            nec::BTN_START => Btn(Button::Start),\n            nec::BTN_MODE => Btn(Button::Mode),\n            nec::BTN_LTHUMB => Btn(Button::LeftThumb),\n            nec::BTN_RTHUMB => Btn(Button::RightThumb),\n            nec::BTN_DPAD_UP => Btn(Button::DPadUp),\n            nec::BTN_DPAD_DOWN => Btn(Button::DPadDown),\n            nec::BTN_DPAD_LEFT => Btn(Button::DPadLeft),\n            nec::BTN_DPAD_RIGHT => Btn(Button::DPadRight),\n\n            nec::AXIS_LT => Btn(Button::LeftTrigger),\n            nec::AXIS_RT => Btn(Button::RightTrigger),\n            nec::AXIS_LT2 => Btn(Button::LeftTrigger2),\n            nec::AXIS_RT2 => Btn(Button::RightTrigger2),\n\n            nec::AXIS_LSTICKX => Axis(Ax::LeftStickX),\n            nec::AXIS_LSTICKY => Axis(Ax::LeftStickY),\n            nec::AXIS_LEFTZ => Axis(Ax::LeftZ),\n            nec::AXIS_RSTICKX => Axis(Ax::RightStickX),\n            nec::AXIS_RSTICKY => Axis(Ax::RightStickY),\n            nec::AXIS_RIGHTZ => Axis(Ax::RightZ),\n            nec::AXIS_DPADX => Axis(Ax::DPadX),\n            nec::AXIS_DPADY => Axis(Ax::DPadY)\n        ];\n\n        // Remove all mappings that don't have corresponding element in gamepad. Partial fix to #83\n        let axes = [\n            nec::AXIS_DPADX,\n            nec::AXIS_DPADY,\n            nec::AXIS_LEFTZ,\n            nec::AXIS_LSTICKX,\n            nec::AXIS_LSTICKY,\n            nec::AXIS_RSTICKX,\n            nec::AXIS_RSTICKY,\n            nec::AXIS_LT,\n            nec::AXIS_LT2,\n            nec::AXIS_RT,\n            nec::AXIS_RT2,\n            nec::AXIS_RIGHTZ,\n        ];\n        let btns = [\n            nec::BTN_SOUTH,\n            nec::BTN_NORTH,\n            nec::BTN_WEST,\n            nec::BTN_WEST,\n            nec::BTN_C,\n            nec::BTN_Z,\n            nec::BTN_LT,\n            nec::BTN_LT2,\n            nec::BTN_RT,\n            nec::BTN_RT2,\n            nec::BTN_SELECT,\n            nec::BTN_START,\n            nec::BTN_MODE,\n            nec::BTN_LTHUMB,\n            nec::BTN_RTHUMB,\n            nec::BTN_DPAD_DOWN,\n            nec::BTN_DPAD_LEFT,\n            nec::BTN_DPAD_RIGHT,\n            nec::BTN_DPAD_UP,\n        ];\n\n        for axis in &axes {\n            if !gamepad.axes().contains(axis) {\n                mappings.remove(axis);\n            }\n        }\n\n        for btn in &btns {\n            if !gamepad.buttons().contains(btn) {\n                mappings.remove(btn);\n            }\n        }\n\n        Mapping {\n            mappings,\n            name: String::new(),\n            default: true,\n            hats_mapped: 0,\n        }\n    }\n\n    pub fn name(&self) -> &str {\n        &self.name\n    }\n\n    pub fn from_data(\n        data: &MappingData,\n        buttons: &[EvCode],\n        axes: &[EvCode],\n        name: &str,\n        uuid: Uuid,\n    ) -> Result<(Self, String), MappingError> {\n        use crate::constants::*;\n\n        if !Self::is_name_valid(name) {\n            return Err(MappingError::InvalidName);\n        }\n\n        let mut mappings = FnvHashMap::default();\n        let mut sdl_mappings = format!(\"{},{},\", uuid.as_simple(), name);\n\n        {\n            let mut add_button = |ident, ev_code, mapped_btn| {\n                Self::add_button(\n                    ident,\n                    ev_code,\n                    mapped_btn,\n                    buttons,\n                    &mut sdl_mappings,\n                    &mut mappings,\n                )\n            };\n\n            for (button, &ev_code) in &data.buttons {\n                match button as u16 {\n                    BTN_SOUTH => add_button(\"a\", ev_code, Button::South)?,\n                    BTN_EAST => add_button(\"b\", ev_code, Button::East)?,\n                    BTN_WEST => add_button(\"x\", ev_code, Button::West)?,\n                    BTN_NORTH => add_button(\"y\", ev_code, Button::North)?,\n                    BTN_LT => add_button(\"leftshoulder\", ev_code, Button::LeftTrigger)?,\n                    BTN_RT => add_button(\"rightshoulder\", ev_code, Button::RightTrigger)?,\n                    BTN_LT2 => add_button(\"lefttrigger\", ev_code, Button::LeftTrigger2)?,\n                    BTN_RT2 => add_button(\"righttrigger\", ev_code, Button::RightTrigger2)?,\n                    BTN_SELECT => add_button(\"back\", ev_code, Button::Select)?,\n                    BTN_START => add_button(\"start\", ev_code, Button::Start)?,\n                    BTN_MODE => add_button(\"guide\", ev_code, Button::Mode)?,\n                    BTN_LTHUMB => add_button(\"leftstick\", ev_code, Button::LeftThumb)?,\n                    BTN_RTHUMB => add_button(\"rightstick\", ev_code, Button::RightThumb)?,\n                    BTN_DPAD_UP => add_button(\"dpup\", ev_code, Button::DPadUp)?,\n                    BTN_DPAD_DOWN => add_button(\"dpdown\", ev_code, Button::DPadDown)?,\n                    BTN_DPAD_LEFT => add_button(\"dpleft\", ev_code, Button::DPadLeft)?,\n                    BTN_DPAD_RIGHT => add_button(\"dpright\", ev_code, Button::DPadRight)?,\n                    BTN_C => add_button(\"c\", ev_code, Button::C)?,\n                    BTN_Z => add_button(\"z\", ev_code, Button::Z)?,\n                    BTN_UNKNOWN => return Err(MappingError::UnknownElement),\n                    _ => unreachable!(),\n                }\n            }\n        }\n\n        {\n            let mut add_axis = |ident, ev_code, mapped_axis| {\n                Self::add_axis(\n                    ident,\n                    ev_code,\n                    mapped_axis,\n                    axes,\n                    &mut sdl_mappings,\n                    &mut mappings,\n                )\n            };\n\n            for (axis, &ev_code) in &data.axes {\n                match axis as u16 {\n                    AXIS_LSTICKX => add_axis(\"leftx\", ev_code, Axis::LeftStickX)?,\n                    AXIS_LSTICKY => add_axis(\"lefty\", ev_code, Axis::LeftStickY)?,\n                    AXIS_RSTICKX => add_axis(\"rightx\", ev_code, Axis::RightStickX)?,\n                    AXIS_RSTICKY => add_axis(\"righty\", ev_code, Axis::RightStickY)?,\n                    AXIS_LEFTZ => add_axis(\"leftz\", ev_code, Axis::LeftZ)?,\n                    AXIS_RIGHTZ => add_axis(\"rightz\", ev_code, Axis::RightZ)?,\n                    AXIS_UNKNOWN => return Err(MappingError::UnknownElement),\n                    _ => unreachable!(),\n                }\n            }\n        }\n\n        let mapping = Mapping {\n            mappings,\n            name: name.to_owned(),\n            default: false,\n            hats_mapped: 0,\n        };\n\n        Ok((mapping, sdl_mappings))\n    }\n\n    pub fn parse_sdl_mapping(\n        line: &str,\n        buttons: &[EvCode],\n        axes: &[EvCode],\n    ) -> Result<Self, ParseSdlMappingError> {\n        let mut mapping = Mapping::new();\n        let mut parser = Parser::new(line);\n\n        let mut uuid: Option<Uuid> = None;\n        while let Some(token) = parser.next_token() {\n            if let Err(ref e) = token {\n                if e.kind() == &ParserErrorKind::EmptyValue {\n                    continue;\n                }\n            }\n\n            let token = token?;\n\n            match token {\n                Token::Platform(platform) => {\n                    if platform != SDL_PLATFORM_NAME {\n                        warn!(\"Mappings for different platform – {}\", platform);\n                    }\n                }\n                Token::Uuid(v) => uuid = Some(v),\n\n                Token::Name(name) => mapping.name = name.to_owned(),\n                Token::AxisMapping { from, to, .. } => {\n                    let axis = axes.get(from as usize).cloned();\n                    if let Some(axis) = axis {\n                        mapping.mappings.insert(axis, to);\n                    } else {\n                        warn!(\n                            \"SDL-mapping {} {}: Unknown axis a{}\",\n                            uuid.unwrap(),\n                            mapping.name,\n                            from\n                        )\n                    }\n                }\n                Token::ButtonMapping { from, to, .. } => {\n                    let btn = buttons.get(from as usize).cloned();\n\n                    if let Some(btn) = btn {\n                        mapping.mappings.insert(btn, to);\n                    } else {\n                        warn!(\n                            \"SDL-mapping {} {}: Unknown button b{}\",\n                            uuid.unwrap(),\n                            mapping.name,\n                            from\n                        )\n                    }\n                }\n                Token::HatMapping {\n                    hat, direction, to, ..\n                } => {\n                    if hat != 0 {\n                        warn!(\n                            \"Hat mappings are only supported for dpads (requested to map hat \\\n                             {}.{} to {:?}\",\n                            hat, direction, to\n                        );\n                    } else {\n                        // We  don't have anything like \"hat\" in gilrs, so let's jus assume that\n                        // user want to map dpad axes.\n                        //\n                        // We have to add mappings for axes AND buttons, because axis_dpad_to_button\n                        // filter may transform event to button event.\n                        let (from_axis, from_btn) = match direction {\n                            1 => (nec::AXIS_DPADY, nec::BTN_DPAD_UP),\n                            4 => (nec::AXIS_DPADY, nec::BTN_DPAD_DOWN),\n                            2 => (nec::AXIS_DPADX, nec::BTN_DPAD_RIGHT),\n                            8 => (nec::AXIS_DPADX, nec::BTN_DPAD_LEFT),\n                            0 => continue, // FIXME: I have no idea what 0 means here\n                            _ => return Err(ParseSdlMappingError::UnknownHatDirection),\n                        };\n\n                        if to.is_button() {\n                            match to {\n                                AxisOrBtn::Btn(Button::DPadLeft | Button::DPadRight) => {\n                                    mapping\n                                        .mappings\n                                        .insert(from_axis, AxisOrBtn::Axis(Axis::DPadX));\n                                }\n                                AxisOrBtn::Btn(Button::DPadUp | Button::DPadDown) => {\n                                    mapping\n                                        .mappings\n                                        .insert(from_axis, AxisOrBtn::Axis(Axis::DPadY));\n                                }\n                                _ => (),\n                            }\n                            mapping.mappings.insert(from_btn, to);\n                        } else {\n                            mapping.mappings.insert(from_axis, to);\n                        }\n\n                        mapping.hats_mapped |= direction as u8;\n                    }\n                }\n            }\n        }\n\n        Ok(mapping)\n    }\n\n    fn add_button(\n        ident: &str,\n        ev_code: EvCode,\n        mapped_btn: Button,\n        buttons: &[EvCode],\n        sdl_mappings: &mut String,\n        mappings: &mut FnvHashMap<EvCode, AxisOrBtn>,\n    ) -> Result<(), MappingError> {\n        let n_btn = buttons\n            .iter()\n            .position(|&x| x == ev_code)\n            .ok_or(MappingError::InvalidCode(ev::Code(ev_code)))?;\n        let _ = write!(sdl_mappings, \"{}:b{},\", ident, n_btn);\n        mappings.insert(ev_code, AxisOrBtn::Btn(mapped_btn));\n        Ok(())\n    }\n\n    fn add_axis(\n        ident: &str,\n        ev_code: EvCode,\n        mapped_axis: Axis,\n        axes: &[EvCode],\n        sdl_mappings: &mut String,\n        mappings: &mut FnvHashMap<EvCode, AxisOrBtn>,\n    ) -> Result<(), MappingError> {\n        let n_axis = axes\n            .iter()\n            .position(|&x| x == ev_code)\n            .ok_or(MappingError::InvalidCode(ev::Code(ev_code)))?;\n        let _ = write!(sdl_mappings, \"{}:a{},\", ident, n_axis);\n        mappings.insert(ev_code, AxisOrBtn::Axis(mapped_axis));\n        Ok(())\n    }\n\n    fn is_name_valid(name: &str) -> bool {\n        !name.chars().any(|x| x == ',')\n    }\n\n    pub fn map(&self, code: &EvCode) -> Option<AxisOrBtn> {\n        self.mappings.get(code).cloned()\n    }\n\n    pub fn map_rev(&self, el: &AxisOrBtn) -> Option<EvCode> {\n        self.mappings.iter().find(|x| x.1 == el).map(|x| *x.0)\n    }\n\n    pub fn is_default(&self) -> bool {\n        self.default\n    }\n\n    /// Return bit field with mapped hats. Only for mappings created from SDL format this function\n    /// can return non-zero value.\n    pub fn hats_mapped(&self) -> u8 {\n        self.hats_mapped\n    }\n}\n\n#[derive(Clone, PartialEq, Eq, Debug)]\npub enum ParseSdlMappingError {\n    UnknownHatDirection,\n    ParseError(ParserError),\n}\n\nimpl From<ParserError> for ParseSdlMappingError {\n    fn from(f: ParserError) -> Self {\n        ParseSdlMappingError::ParseError(f)\n    }\n}\n\nimpl Error for ParseSdlMappingError {\n    fn source(&self) -> Option<&(dyn Error + 'static)> {\n        if let ParseSdlMappingError::ParseError(ref err) = self {\n            Some(err)\n        } else {\n            None\n        }\n    }\n}\n\nimpl Display for ParseSdlMappingError {\n    fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {\n        match self {\n            ParseSdlMappingError::UnknownHatDirection => {\n                fmt.write_str(\"hat direction wasn't 1, 2, 4 or 8\")\n            }\n            ParseSdlMappingError::ParseError(_) => fmt.write_str(\"parsing error\"),\n        }\n    }\n}\n\n#[derive(Debug)]\npub struct MappingDb {\n    mappings: HashMap<Uuid, String>,\n}\n\nimpl MappingDb {\n    pub fn new() -> Self {\n        MappingDb {\n            mappings: HashMap::new(),\n        }\n    }\n\n    pub fn add_included_mappings(&mut self) {\n        self.insert(include_str!(concat!(\n            env!(\"OUT_DIR\"),\n            PATH_SEPARATOR!(),\n            \"gamecontrollerdb.txt\"\n        )));\n    }\n\n    pub fn add_env_mappings(&mut self) {\n        if let Ok(mapping) = env::var(\"SDL_GAMECONTROLLERCONFIG\") {\n            self.insert(&mapping);\n        }\n    }\n\n    pub fn insert(&mut self, s: &str) {\n        for mapping in s.lines() {\n            let pat = \"platform:\";\n            if let Some(offset) = mapping.find(pat).map(|o| o + pat.len()) {\n                let s = &mapping[offset..];\n                let end = s.find(',').unwrap_or(s.len());\n\n                if &s[..end] != SDL_PLATFORM_NAME {\n                    continue;\n                }\n            }\n\n            mapping\n                .split(',')\n                .next()\n                .and_then(|s| Uuid::parse_str(s).ok())\n                .and_then(|uuid| self.mappings.insert(uuid, mapping.to_owned()));\n        }\n    }\n\n    pub fn get(&self, uuid: Uuid) -> Option<&str> {\n        self.mappings.get(&uuid).map(String::as_ref)\n    }\n\n    pub fn len(&self) -> usize {\n        self.mappings.len()\n    }\n}\n\n/// Stores data used to map gamepad buttons and axes.\n///\n/// After you add all mappings, use\n/// [`Gamepad::set_mapping(…)`](struct.Gamepad.html#method.set_mapping) to change mapping of\n/// existing gamepad.\n///\n/// See `examples/mapping.rs` for more detailed example.\n#[derive(Debug, Clone, Default)]\n// Re-exported as Mapping\npub struct MappingData {\n    buttons: VecMap<EvCode>,\n    axes: VecMap<EvCode>,\n}\n\nimpl MappingData {\n    /// Creates new `Mapping`.\n    pub fn new() -> Self {\n        MappingData {\n            buttons: VecMap::with_capacity(18),\n            axes: VecMap::with_capacity(11),\n        }\n    }\n\n    /// Returns `EvCode` associated with button index.\n    pub fn button(&self, idx: Button) -> Option<ev::Code> {\n        self.buttons.get(idx as usize).cloned().map(ev::Code)\n    }\n\n    /// Returns `EvCode` associated with axis index.\n    pub fn axis(&self, idx: Axis) -> Option<ev::Code> {\n        self.axes.get(idx as usize).cloned().map(ev::Code)\n    }\n\n    /// Inserts new button mapping.\n    pub fn insert_btn(&mut self, from: ev::Code, to: Button) -> Option<ev::Code> {\n        self.buttons.insert(to as usize, from.0).map(ev::Code)\n    }\n\n    /// Inserts new axis mapping.\n    pub fn insert_axis(&mut self, from: ev::Code, to: Axis) -> Option<ev::Code> {\n        self.axes.insert(to as usize, from.0).map(ev::Code)\n    }\n\n    /// Removes button and returns associated `NativEvCode`.\n    pub fn remove_button(&mut self, idx: Button) -> Option<ev::Code> {\n        self.buttons.remove(idx as usize).map(ev::Code)\n    }\n\n    /// Removes axis and returns associated `NativEvCode`.\n    pub fn remove_axis(&mut self, idx: Axis) -> Option<ev::Code> {\n        self.axes.remove(idx as usize).map(ev::Code)\n    }\n}\n\n/// The error type for functions related to gamepad mapping.\n#[derive(Copy, Clone, Debug, PartialEq, Eq)]\n#[non_exhaustive]\npub enum MappingError {\n    /// Gamepad does not have element referenced by `EvCode`.\n    InvalidCode(ev::Code),\n    /// Name contains comma (',').\n    InvalidName,\n    /// This function is not implemented for current platform.\n    NotImplemented,\n    /// Gamepad is not connected.\n    NotConnected,\n    /// Same gamepad element is referenced by axis and button.\n    DuplicatedEntry,\n    /// `Mapping` with `Button::Unknown` or `Axis::Unknown`.\n    UnknownElement,\n    /// `Mapping` have button or axis that are not present in SDL2.\n    NotSdl2Compatible,\n}\n\nimpl Error for MappingError {}\n\nimpl Display for MappingError {\n    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {\n        let sbuf;\n        let s = match self {\n            MappingError::InvalidCode(code) => {\n                sbuf = format!(\"gamepad does not have element with {}\", code);\n                sbuf.as_ref()\n            }\n            MappingError::InvalidName => \"name can not contain comma\",\n            MappingError::NotImplemented => {\n                \"current platform does not implement setting custom mappings\"\n            }\n            MappingError::NotConnected => \"gamepad is not connected\",\n            MappingError::DuplicatedEntry => {\n                \"same gamepad element is referenced by axis and button\"\n            }\n            MappingError::UnknownElement => \"Button::Unknown and Axis::Unknown are not allowed\",\n            MappingError::NotSdl2Compatible => \"one of buttons or axes is not compatible with SDL2\",\n        };\n\n        f.write_str(s)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::ev::{Axis, Button};\n    use gilrs_core::native_ev_codes as nec;\n    use gilrs_core::EvCode;\n    use uuid::Uuid;\n    // Do not include platform, mapping from (with UUID modified)\n    // https://github.com/gabomdq/SDL_GameControllerDB/blob/master/gamecontrollerdb.txt\n    const TEST_STR: &str = \"03000000260900008888000000010001,GameCube {WiseGroup USB \\\n                            box},a:b0,b:b2,y:b3,x:b1,start:b7,rightshoulder:b6,dpup:h0.1,dpleft:\\\n                            h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,\\\n                            lefttrigger:a4,righttrigger:a5,\";\n\n    const BUTTONS: [EvCode; 15] = [\n        nec::BTN_SOUTH,\n        nec::BTN_EAST,\n        nec::BTN_C,\n        nec::BTN_NORTH,\n        nec::BTN_WEST,\n        nec::BTN_Z,\n        nec::BTN_LT,\n        nec::BTN_RT,\n        nec::BTN_LT2,\n        nec::BTN_RT2,\n        nec::BTN_SELECT,\n        nec::BTN_START,\n        nec::BTN_MODE,\n        nec::BTN_LTHUMB,\n        nec::BTN_RTHUMB,\n    ];\n\n    const AXES: [EvCode; 12] = [\n        nec::AXIS_LSTICKX,\n        nec::AXIS_LSTICKY,\n        nec::AXIS_LEFTZ,\n        nec::AXIS_RSTICKX,\n        nec::AXIS_RSTICKY,\n        nec::AXIS_RIGHTZ,\n        nec::AXIS_DPADX,\n        nec::AXIS_DPADY,\n        nec::AXIS_RT,\n        nec::AXIS_LT,\n        nec::AXIS_RT2,\n        nec::AXIS_LT2,\n    ];\n\n    #[test]\n    fn mapping() {\n        Mapping::parse_sdl_mapping(TEST_STR, &BUTTONS, &AXES).unwrap();\n    }\n\n    #[test]\n    fn from_data() {\n        let uuid = Uuid::nil();\n        let name = \"Best Gamepad\";\n        let buttons = BUTTONS.iter().cloned().map(ev::Code).collect::<Vec<_>>();\n        let axes = AXES.iter().cloned().map(ev::Code).collect::<Vec<_>>();\n\n        let mut data = MappingData::new();\n        data.insert_axis(axes[0], Axis::LeftStickX);\n        data.insert_axis(axes[1], Axis::LeftStickY);\n        data.insert_axis(axes[2], Axis::LeftZ);\n        data.insert_axis(axes[3], Axis::RightStickX);\n        data.insert_axis(axes[4], Axis::RightStickY);\n        data.insert_axis(axes[5], Axis::RightZ);\n\n        data.insert_btn(buttons[0], Button::South);\n        data.insert_btn(buttons[1], Button::East);\n        data.insert_btn(buttons[3], Button::North);\n        data.insert_btn(buttons[4], Button::West);\n        data.insert_btn(buttons[5], Button::Select);\n        data.insert_btn(buttons[6], Button::Start);\n        data.insert_btn(buttons[7], Button::DPadDown);\n        data.insert_btn(buttons[8], Button::DPadLeft);\n        data.insert_btn(buttons[9], Button::RightThumb);\n\n        let (mappings, sdl_mappings) =\n            Mapping::from_data(&data, &BUTTONS, &AXES, name, uuid).unwrap();\n        let sdl_mappings = Mapping::parse_sdl_mapping(&sdl_mappings, &BUTTONS, &AXES).unwrap();\n        assert_eq!(mappings, sdl_mappings);\n\n        let incorrect_mappings = Mapping::from_data(&data, &BUTTONS, &AXES, \"Inval,id name\", uuid);\n        assert_eq!(Err(MappingError::InvalidName), incorrect_mappings);\n\n        data.insert_btn(ev::Code(nec::BTN_DPAD_RIGHT), Button::DPadRight);\n        let incorrect_mappings = Mapping::from_data(&data, &BUTTONS, &AXES, name, uuid);\n        assert_eq!(\n            Err(MappingError::InvalidCode(ev::Code(nec::BTN_DPAD_RIGHT))),\n            incorrect_mappings\n        );\n\n        data.insert_btn(ev::Code(BUTTONS[3]), Button::Unknown);\n        let incorrect_mappings = Mapping::from_data(&data, &BUTTONS, &AXES, name, uuid);\n        assert_eq!(Err(MappingError::UnknownElement), incorrect_mappings);\n    }\n\n    #[test]\n    fn with_mappings() {\n        let mappings = format!(\n            \"\\nShould be ignored\\nThis also should,be ignored\\n\\n{}\",\n            TEST_STR\n        );\n        let mut db = MappingDb::new();\n        db.add_included_mappings();\n        db.insert(&mappings);\n\n        assert_eq!(\n            Some(TEST_STR),\n            db.get(Uuid::parse_str(\"03000000260900008888000000010001\").unwrap())\n        );\n    }\n}\n"
  },
  {
    "path": "gilrs/src/mapping/parser.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\nuse std::error::Error as StdError;\nuse std::fmt::{self, Display};\n\nuse uuid::Uuid;\n\nuse crate::ev::{Axis, AxisOrBtn, Button};\n\n// Must be sorted!\nstatic AXES_SDL: [&str; 31] = [\n    \"a\",\n    \"b\",\n    \"back\",\n    \"c\",\n    \"dpdown\",\n    \"dpleft\",\n    \"dpright\",\n    \"dpup\",\n    \"guide\",\n    \"leftshoulder\",\n    \"leftstick\",\n    \"lefttrigger\",\n    \"leftx\",\n    \"lefty\",\n    \"leftz\",\n    \"misc1\",\n    \"paddle1\",\n    \"paddle2\",\n    \"paddle3\",\n    \"paddle4\",\n    \"rightshoulder\",\n    \"rightstick\",\n    \"righttrigger\",\n    \"rightx\",\n    \"righty\",\n    \"rightz\",\n    \"start\",\n    \"touchpad\",\n    \"x\",\n    \"y\",\n    \"z\",\n];\nstatic AXES: [AxisOrBtn; 31] = [\n    AxisOrBtn::Btn(Button::South),\n    AxisOrBtn::Btn(Button::East),\n    AxisOrBtn::Btn(Button::Select),\n    AxisOrBtn::Btn(Button::C),\n    AxisOrBtn::Btn(Button::DPadDown),\n    AxisOrBtn::Btn(Button::DPadLeft),\n    AxisOrBtn::Btn(Button::DPadRight),\n    AxisOrBtn::Btn(Button::DPadUp),\n    AxisOrBtn::Btn(Button::Mode),\n    AxisOrBtn::Btn(Button::LeftTrigger),\n    AxisOrBtn::Btn(Button::LeftThumb),\n    AxisOrBtn::Btn(Button::LeftTrigger2),\n    AxisOrBtn::Axis(Axis::LeftStickX),\n    AxisOrBtn::Axis(Axis::LeftStickY),\n    AxisOrBtn::Axis(Axis::LeftZ),\n    AxisOrBtn::Btn(Button::Unknown),\n    AxisOrBtn::Btn(Button::Unknown),\n    AxisOrBtn::Btn(Button::Unknown),\n    AxisOrBtn::Btn(Button::Unknown),\n    AxisOrBtn::Btn(Button::Unknown),\n    AxisOrBtn::Btn(Button::RightTrigger),\n    AxisOrBtn::Btn(Button::RightThumb),\n    AxisOrBtn::Btn(Button::RightTrigger2),\n    AxisOrBtn::Axis(Axis::RightStickX),\n    AxisOrBtn::Axis(Axis::RightStickY),\n    AxisOrBtn::Axis(Axis::RightZ),\n    AxisOrBtn::Btn(Button::Start),\n    AxisOrBtn::Btn(Button::Unknown),\n    AxisOrBtn::Btn(Button::West),\n    AxisOrBtn::Btn(Button::North),\n    AxisOrBtn::Btn(Button::Z),\n];\n\npub struct Parser<'a> {\n    data: &'a str,\n    pos: usize,\n    state: State,\n}\n\nimpl<'a> Parser<'a> {\n    pub fn new(mapping: &'a str) -> Self {\n        Parser {\n            data: mapping,\n            pos: 0,\n            state: State::Uuid,\n        }\n    }\n\n    pub fn next_token(&mut self) -> Option<Result<Token<'_>, Error>> {\n        if self.pos >= self.data.len() {\n            None\n        } else {\n            Some(match self.state {\n                State::Uuid => self.parse_uuid(),\n                State::Name => self.parse_name(),\n                State::KeyVal => self.parse_key_val(),\n                State::Invalid => Err(Error::new(ErrorKind::InvalidParserState, self.pos)),\n            })\n        }\n    }\n\n    fn parse_uuid(&mut self) -> Result<Token<'_>, Error> {\n        let next_comma = self.next_comma_or_end();\n        let uuid_field = &self.data[self.pos..next_comma];\n        let uuid = if uuid_field == \"xinput\" {\n            Ok(Token::Uuid(Uuid::nil()))\n        } else {\n            Uuid::parse_str(uuid_field)\n                .map(Token::Uuid)\n                .map_err(|_| Error::new(ErrorKind::InvalidGuid, self.pos))\n        };\n\n        if uuid.is_err() {\n            self.state = State::Invalid;\n        } else if next_comma == self.data.len() {\n            self.state = State::Invalid;\n\n            return Err(Error::new(ErrorKind::UnexpectedEnd, self.pos));\n        } else {\n            self.state = State::Name;\n            self.pos = next_comma + 1;\n        }\n\n        uuid\n    }\n\n    fn parse_name(&mut self) -> Result<Token<'_>, Error> {\n        let next_comma = self.next_comma_or_end();\n        let name = &self.data[self.pos..next_comma];\n\n        self.state = State::KeyVal;\n        self.pos = next_comma + 1;\n\n        Ok(Token::Name(name))\n    }\n\n    fn parse_key_val(&mut self) -> Result<Token<'_>, Error> {\n        let next_comma = self.next_comma_or_end();\n        let pair = &self.data[self.pos..next_comma];\n        let pos = self.pos;\n        self.pos = next_comma + 1;\n\n        let mut split = pair.split(':');\n        let key = split\n            .next()\n            .ok_or_else(|| Error::new(ErrorKind::InvalidKeyValPair, pos))?;\n        let value = split\n            .next()\n            .ok_or_else(|| Error::new(ErrorKind::InvalidKeyValPair, pos))?;\n\n        if split.next().is_some() {\n            return Err(Error::new(ErrorKind::InvalidKeyValPair, pos));\n        }\n\n        if value.is_empty() {\n            return Err(Error::new(ErrorKind::EmptyValue, pos));\n        }\n\n        if key == \"platform\" {\n            return Ok(Token::Platform(value));\n        }\n\n        let mut input = AxisRange::Full;\n        let mut output = AxisRange::Full;\n        let mut inverted = false;\n        let mut is_axis = false;\n\n        let key = match key.get(0..1) {\n            Some(\"+\") => {\n                output = AxisRange::UpperHalf;\n                &key[1..]\n            }\n            Some(\"-\") => {\n                output = AxisRange::LowerHalf;\n                &key[1..]\n            }\n            _ => key,\n        };\n\n        let from = match value.get(0..1) {\n            Some(\"+\") if value.get(1..2) == Some(\"a\") => {\n                is_axis = true;\n                input = AxisRange::UpperHalf;\n\n                if value.get((value.len() - 1)..) == Some(\"~\") {\n                    inverted = true;\n\n                    &value[2..(value.len() - 1)]\n                } else {\n                    &value[2..]\n                }\n            }\n            Some(\"-\") if value.get(1..2) == Some(\"a\") => {\n                is_axis = true;\n                input = AxisRange::LowerHalf;\n\n                if value.get((value.len() - 1)..) == Some(\"~\") {\n                    inverted = true;\n\n                    &value[2..(value.len() - 1)]\n                } else {\n                    &value[2..]\n                }\n            }\n            Some(\"a\") => {\n                is_axis = true;\n\n                if value.get((value.len() - 1)..) == Some(\"~\") {\n                    inverted = true;\n\n                    &value[1..(value.len() - 1)]\n                } else {\n                    &value[1..]\n                }\n            }\n            Some(\"b\") => &value[1..],\n            Some(\"h\") => {\n                let dot_idx = value\n                    .find('.')\n                    .ok_or_else(|| Error::new(ErrorKind::InvalidValue, pos))?;\n                let hat = value[1..dot_idx]\n                    .parse()\n                    .map_err(|_| Error::new(ErrorKind::InvalidValue, pos + 1))?;\n                let direction = value\n                    .get((dot_idx + 1)..)\n                    .and_then(|s| s.parse().ok())\n                    .ok_or_else(|| Error::new(ErrorKind::InvalidValue, pos + dot_idx + 1))?;\n\n                let idx = AXES_SDL\n                    .binary_search(&key)\n                    .map_err(|_| Error::new(ErrorKind::UnknownButton, pos))?;\n\n                return Ok(Token::HatMapping {\n                    hat,\n                    direction,\n                    to: AXES[idx],\n                    output,\n                });\n            }\n            _ => return Err(Error::new(ErrorKind::InvalidValue, pos)),\n        }\n        .parse::<u16>()\n        .map_err(|_| Error::new(ErrorKind::InvalidValue, pos))?;\n\n        if is_axis {\n            let idx = AXES_SDL\n                .binary_search(&key)\n                .map_err(|_| Error::new(ErrorKind::UnknownAxis, pos))?;\n\n            Ok(Token::AxisMapping {\n                from,\n                to: AXES[idx],\n                input,\n                output,\n                inverted,\n            })\n        } else {\n            let idx = AXES_SDL\n                .binary_search(&key)\n                .map_err(|_| Error::new(ErrorKind::UnknownButton, pos))?;\n\n            Ok(Token::ButtonMapping {\n                from,\n                to: AXES[idx],\n                output,\n            })\n        }\n    }\n\n    fn next_comma_or_end(&self) -> usize {\n        self.data[self.pos..]\n            .find(',')\n            .map(|x| x + self.pos)\n            .unwrap_or_else(|| self.data.len())\n    }\n}\n\n#[derive(Debug)]\npub enum Token<'a> {\n    Uuid(Uuid),\n    Platform(&'a str),\n    Name(&'a str),\n    #[allow(dead_code)]\n    AxisMapping {\n        from: u16,\n        to: AxisOrBtn,\n        input: AxisRange,\n        output: AxisRange,\n        inverted: bool,\n    },\n    ButtonMapping {\n        from: u16,\n        to: AxisOrBtn,\n        #[allow(dead_code)]\n        output: AxisRange,\n    },\n    // This is just SDL representation, we will convert this to axis mapping later\n    HatMapping {\n        hat: u16,\n        // ?\n        direction: u16,\n        to: AxisOrBtn,\n        #[allow(dead_code)]\n        output: AxisRange,\n    },\n}\n\n#[repr(u8)]\n#[derive(Debug)]\npub enum AxisRange {\n    LowerHalf,\n    UpperHalf,\n    Full,\n}\n\n#[derive(Copy, Clone, Eq, PartialEq)]\nenum State {\n    Uuid,\n    Name,\n    KeyVal,\n    Invalid,\n}\n\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct Error {\n    pub(crate) position: usize,\n    kind: ErrorKind,\n}\n\nimpl Error {\n    pub fn new(kind: ErrorKind, position: usize) -> Self {\n        Error { position, kind }\n    }\n\n    pub fn kind(&self) -> &ErrorKind {\n        &self.kind\n    }\n}\n\n#[derive(Debug, Clone, PartialEq, Eq)]\n#[non_exhaustive]\npub enum ErrorKind {\n    InvalidGuid,\n    InvalidKeyValPair,\n    InvalidValue,\n    EmptyValue,\n    UnknownAxis,\n    UnknownButton,\n    InvalidParserState,\n    UnexpectedEnd,\n}\n\nimpl StdError for Error {}\n\nimpl Display for Error {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        let s = match self.kind {\n            ErrorKind::InvalidGuid => \"GUID is invalid\",\n            ErrorKind::InvalidKeyValPair => \"expected key value pair\",\n            ErrorKind::InvalidValue => \"value is not valid\",\n            ErrorKind::EmptyValue => \"value is empty\",\n            ErrorKind::UnknownAxis => \"invalid axis name\",\n            ErrorKind::UnknownButton => \"invalid button name\",\n            ErrorKind::InvalidParserState => \"attempt to parse after unrecoverable error\",\n            ErrorKind::UnexpectedEnd => \"mapping does not have all required fields\",\n        };\n\n        f.write_fmt(format_args!(\"{} at {}\", s, self.position))\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use crate::mapping::parser::{ErrorKind, Parser};\n    use crate::utils::PATH_SEPARATOR;\n\n    #[test]\n    fn test_all_sdl_mappings_for_parse_errors() {\n        let included_mappings = include_str!(concat!(\n            env!(\"OUT_DIR\"),\n            PATH_SEPARATOR!(),\n            \"gamecontrollerdb.txt\"\n        ))\n        .lines();\n\n        let mut errors = 0;\n        let mut index = 0;\n        for line in included_mappings {\n            let mut parser = Parser::new(line);\n\n            while let Some(token) = parser.next_token() {\n                if let Err(ref e) = token {\n                    if e.kind() != &ErrorKind::EmptyValue {\n                        errors += 1;\n                        println!(\"{e:?}\");\n                        println!(\n                            \"{}: {} (...) {}\\n\",\n                            index,\n                            line.chars().take(50).collect::<String>(),\n                            line.chars().skip(e.position).take(15).collect::<String>()\n                        );\n\n                        if e.kind() == &ErrorKind::InvalidParserState {\n                            break;\n                        }\n                    }\n                }\n                index += 1;\n            }\n        }\n        assert_eq!(errors, 0);\n    }\n}\n"
  },
  {
    "path": "gilrs/src/utils.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\n\npub use gilrs_core::utils::*;\n\n/// Like `(a: f32 / b).ceil()` but for integers.\npub fn ceil_div(a: u32, b: u32) -> u32 {\n    if a == 0 {\n        0\n    } else {\n        1 + ((a - 1) / b)\n    }\n}\n\npub fn clamp(x: f32, min: f32, max: f32) -> f32 {\n    x.clamp(min, max)\n}\n\n#[cfg(path_separator = \"backslash\")]\nmacro_rules! PATH_SEPARATOR {\n    () => {\n        r\"\\\"\n    };\n}\n\n#[cfg(path_separator = \"slash\")]\nmacro_rules! PATH_SEPARATOR {\n    () => {\n        r\"/\"\n    };\n}\n\npub(crate) use PATH_SEPARATOR;\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn t_clamp() {\n        assert_eq!(clamp(-1.0, 0.0, 1.0), 0.0);\n        assert_eq!(clamp(0.5, 0.0, 1.0), 0.5);\n        assert_eq!(clamp(2.0, 0.0, 1.0), 1.0);\n    }\n}\n"
  },
  {
    "path": "gilrs-core/CHANGELOG.md",
    "content": "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----------\n\n### Changed\n\n- Updated `windows` and `nix` crates\n\nv0.6.4 - 2025-04-06\n----------\n\n### Changed\n\n- Updated `windows` crate\n\nv0.6.3 - 2025-03-10\n----------\n\n### Fixed\n\n- Fixed panic on macOS when gamepad’s vec had unexpected length after failing to open a device\n\n### Changed\n\n- Updated `windows` crate\n\nv0.6.2 - 2025-02-09\n----------\n\n### Fixed\n\n- Fixed possible panic on Windows when `NonRoamableId()` could return error.\n\n### Changed\n\n- Minimal supported Rust version is now 1.80.\n\nv0.6.1 - 2025-01-13\n----------\n\n### Changed\n\n- Minimal supported Rust version is now 1.74\n- Updated `windows` crate\n\nv0.6.0 - 2024-09-15\n----------\n\n### Breaking changes\n\n- Mark Error enums, `EventType` and `Event` as `non_exhaustive`\n\n### Changed\n\n- Minimal supported Rust version is now 1.73\n- Updated dependencies\n\nv0.5.15 - 2024-08-25\n----------\n\n### Fixed\n\n- wasm: Fixed panic when browser assigned unexpected gamepad ID\n- windows: Fixed panic when receiving connected/disconnected events after instance of `Gilrs`\n  was dropped.\n- windows: Don’t panic on Reading::update() returning error\n\nv0.5.13 - 2024-07-08\n----------\n\n### Changed\n\n- Updated `windows` crate\n\nv0.5.12 - 2024-06-15\n----------\n\n### Fixed\n\n- Fixed building on FreeBSD and DragonFly by not using linux implementation\n\n### Changed\n\n- Updated dependencies\n\nv0.5.11 - 2024-03-06\n----------\n\n### Added\n\n- Added `vendor_id()` and `product_id()` to `Gamepad`.\n\n### Changed\n\n- Updated `windows` crate to 0.54.\n\nv0.5.10 - 2023-12-17\n----------\n\n### Changed\n\n- Updated `windows` crate to 0.52.\n\nv0.5.9 - 2023-11-13\n----------\n\n### Fixed\n\n- Disabled unnecessary default features for `inotify`.\n\nv0.5.8 - 2023-11-11\n----------\n\n### Added\n\n- Flatpak is now supported by using inotify instead of udev. (!104)\n\n### Changed\n\n- All thread spawned by gilrs are now named. (!102)\n- MSRV is now 1.65.\n\n### Fixed\n\n- Linux: Fixed delay in Gilrs::new by limiting udev scan to the input\n  subsystem. (!101)\n\n### Fixed\n\nv0.5.7 - 2023-08-22\n----------\n\n### Fixed\n\n- windows: Join wgi thread on `Gilrs`'s drop\n- wasm: Fix trigger2 only sending binary values\n\n## Changed\n\n- Update `windows` to 0.51\n\nv0.5.6 - 2023-06-19\n----------\n\n### Fixed\n\n- Linux: fixed panic when calling `get_power_info` on disconnected gamepad.\n\nv0.5.5 - 2023-04-23\n----------\n\n### Added\n\n- `Gilrs::next_event_blocking()`\n\nv0.5.4 - 2023-04-03\n----------\n\n### Changed\n\n- Updated `io-kit-sys`, `windows` and `nix`\n\nv0.5.3 - 2023-03-29\n----------\n\n### Changed\n\n- Updated `windows` to 0.44\n\n### Fixed\n\n- web: Fixed handling of disconnected gamepads\n\nv0.5.2 - 2022-12-16\n----------\n\n### Changed\n\n- `Gilrs` is now `Send` on Linux.\n\n### Fixed\n\n- Crash when app is launched through steam on Windows (see\n  https://github.com/microsoft/windows-rs/issues/2252 for details).\n\nv0.5.1 - 2022-11-13\n-------------------\n\n### Fixed\n\n- macOS: Fixed that hat axes were sometimes added before other axes breaking\n  SDL mappings.\n- web: Fixed swapped north and west buttons for gamepads with \"standard\"\n  mapping\n\nv0.5.0 - 2022-11-06\n--------------------\n\n### Changed\n\n- Windows now defaults to using Windows Gaming Input instead of xinput.\n\n  If you need to use xInput you can disable the `wgi` feature (It's enabled by\n  default) and enable the `xinput` feature.\n  ``` toml\n  gilrs-core = {version = \"0.5.0\", default-features = false, features = [\"wgi\"]}\n  ```\n- Apps on Windows will now require a focused window to receive inputs by\n  default.\n\n  This is a limitation of Windows Gaming Input. It requires an in focus Window\n  be associated with the process to receive events. You can still switch back\n  to using xInput by turning off default features and enabling the `xinput`\n  feature.\n\n- Minimal supported rust version is now 1.64.\n\n### Fixed\n\n- `Gamepad::axes()` on macos now also returns \"hat\" axes. This should fix dpad\n  on single Switch Joy-Con.\n\nv0.4.1 - 2022-05-29\n-------------------\n\n### Changed\n\n- Updated io-kit-sys to 0.2 and core-foundation to 0.9 (@jtakakura).\n- Reduced numer of enabled features for nix crate (@rtzoeller).\n\nv0.4.0 - 2022-05-22\n-------------------\n\n### Changed\n\n- wasm: web-sys/wasm-bindgen is now used by default, dependency on stdweb\n  and `wasm-bindgen` feature are removed.\n- Minimal supported rust version is now 1.56.\n- Updated `uuid` and `nix` to current version.\n\n### Fixed\n\n- wasm: `next_event()` no longer panic if `getGamepads()` is not available.\n\nv0.3.2 - 2021-12-30\n-------------------\n\n### Changed\n\n- Updated dependencies\n\nv0.3.1 - 2021-03-30\n-------------------\n\n### Added\n\n- Add support for wasm-bindgen (@coolreader18)\n\nv0.3.0 - 2020-10-09\n-------------------\n\n### Added\n\n- macos: dpad is supported as a set of dpad axes (gilrs filters dpad axes to\n  dpad buttons) (@cleancut).\n\n### Changed\n\n- Minimal supported version is now 1.40\n\nv0.2.6 - 2020-05-11\n-------------------\n\nFixed compilation on musl.\n\nv0.2.5 - 2019-11-30\n-------------------\n\nUpdated dependencies.\n\nv0.2.4 - 2019-09-05\n-------------------\n\n### Fixed\n\n- Fixed compilation on platforms with dummy impl\n\nv0.2.3 - 2019-08-06\n-------------------\n\n### Fixed\n\n- xinput: Removed unneeded logging\n- macos: `IS_Y_AXIS_REVERSED` is now correctly set to `true`\n- macos: Fixed UUID calculation\n\nv0.2.2 - 2019-04-06\n-------------------\n\n### Changed\n\n- Windows: XInput is now dynamically loaded using rusty-xinput\n\n### Fixed\n\n- xinput: incorrect `is_connected()` after hotplugging\n- wasm: Incorrect gamepad IDs in `Disconnected` event (@ryanisaacg)\n\nv0.2.1 - 2019-02-25\n-------------------\n\n### Fixed\n\n- Compilation error on macOS\n\nv0.2.0 - 2019-02-21\n-------------------\n\n### Added\n\n- Initial support for macOS (@jtakakura). There are still some functionality\n  missing, check related issues in #58.\n- Wasm support, using stdweb (@ryanisaacg).\n\n### Changed\n\n- `AxisInfo::deadzone` is now a `Option`.\n- Minimal supported version is now 1.31.1. The crate can still be build with\n  older rustc, but it may change during next patch release.\n\n### Removed\n\n- `AxisInfo::deadzone()` function.\n\n### Fixed\n\n- xinput: Incorrect gamepad ID when more than one gamepad is connected (\n  @DTibbs).\n"
  },
  {
    "path": "gilrs-core/Cargo.toml",
    "content": "[package]\nname = \"gilrs-core\"\nversion = \"0.6.6\"\nauthors = [\"Mateusz Sieczko <arvamer@gmail.com>\"]\nlicense = \"Apache-2.0/MIT\"\ndescription = \"Minimal event-based abstraction for working with gamepads\"\ndocumentation = \"https://docs.rs/gilrs-core/\"\nrepository = \"https://gitlab.com/gilrs-project/gilrs\"\nreadme = \"README.md\"\nkeywords = [\"gamepad\", \"joystick\", \"input\"]\ncategories = [\"game-engines\"]\nedition = \"2021\"\nrust-version = \"1.80.0\"\n\n[dependencies]\nuuid = \"1.0.0\"\nlog = \"0.4.1\"\nserde = { version = \"1.0\", features = [\"derive\"], optional = true }\n\n\n[dev-dependencies]\nenv_logger = \"0.11.5\"\n\n[target.'cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\"))'.dependencies]\nlibudev-sys = \"0.1.4\"\nlibc = \"0.2\"\nnix = { version = \"0.30.1\", default-features = false, features = [\"ioctl\", \"event\"] }\nvec_map = \"0.8\"\ninotify = { version = \"0.11.0\", default-features = false }\n\n[target.'cfg(target_os = \"macos\")'.dependencies]\nobjc2-core-foundation = { version = \"0.3.2\", default-features = false, features = [\n    \"std\",\n    \"CFArray\",\n    \"CFDictionary\",\n    \"CFNumber\",\n    \"CFString\",\n    \"CFRunLoop\",\n] }\nobjc2-io-kit = { version = \"0.3.2\", default-features = false, features = [\n    \"std\",\n    \"libc\",\n    \"hid\",\n] }\nvec_map = \"0.8\"\n\n[target.'cfg(target_os = \"windows\")'.dependencies]\nwinapi = { version = \"0.3.4\", features = [\"xinput\"], optional = true }\nrusty-xinput = { version = \"1.2.0\", optional = true }\nwindows = { version = \">=0.44, <=0.62\", optional = true, features = [\n    \"Gaming_Input\",\n    \"Foundation_Collections\",\n    \"Devices_Power\",\n    \"System_Power\",\n    \"Gaming_Input_ForceFeedback\",\n] }\n\n[target.'cfg(target_arch = \"wasm32\")'.dependencies]\njs-sys = { version = \"0.3\" }\nweb-sys = { version = \"0.3\", features = [\n    \"Gamepad\",\n    \"GamepadButton\",\n    \"GamepadMappingType\",\n    \"Window\",\n    \"Navigator\",\n    \"DomException\",\n] }\nwasm-bindgen = \"0.2\"\n\n[package.metadata.docs.rs]\nfeatures = [\"serde\"]\n\n[features]\ndefault = [\"wgi\"]\nserde-serialize = [\"serde\"]\nxinput = [\"rusty-xinput\", \"winapi\"]\nwgi = [\"windows\"]\n"
  },
  {
    "path": "gilrs-core/README.md",
    "content": "GilRs Core\n==========\n\n[![pipeline status](https://gitlab.com/gilrs-project/gilrs/badges/master/pipeline.svg)](https://gitlab.com/gilrs-project/gilrs-core/commits/master)\n[![Minimum rustc version](https://img.shields.io/badge/rustc-1.64.0+-yellow.svg)](https://gitlab.com/gilrs-project/gilrs)\n\nThis library is minimal event-based abstraction for working with gamepads. If\nyou are looking for something more high level, take a look at `gilrs` crate.\n\nPlatform specific notes\n======================\n\nLinux\n-----\n\nOn Linux, GilRs read (and write, in case of force feedback) directly from\nappropriate `/dev/input/event*` file. This means that user has to have read and\nwrite access to this file. On most distros it shouldn’t be a problem, but if\nit is, you will have to create udev rule.\n\nTo build GilRs, you will need pkg-config and libudev .pc file. On some\ndistributions this file is packaged in separate archive (for example\n`libudev-dev` in Debian).\n\nLicense\n=======\n\nThis project is licensed under the terms of both the Apache License (Version\n2.0) and the MIT license. See LICENSE-APACHE and LICENSE-MIT for details.\n"
  },
  {
    "path": "gilrs-core/examples/ev_core.rs",
    "content": "use gilrs_core::Gilrs;\n\nfn main() {\n    env_logger::init();\n\n    let mut gilrs = Gilrs::new().unwrap();\n    loop {\n        while let Some(ev) = gilrs.next_event_blocking(None) {\n            println!(\"{:0x?}\", ev);\n        }\n    }\n}\n"
  },
  {
    "path": "gilrs-core/src/lib.rs",
    "content": "#[macro_use]\nextern crate log;\n\nuse std::fmt;\nuse std::fmt::Display;\nuse std::fmt::Formatter;\n\nuse std::error;\nuse std::time::Duration;\nuse std::time::SystemTime;\n\nmod platform;\npub mod utils;\n\n/// True, if Y axis of sticks commonly points downwards.\npub const IS_Y_AXIS_REVERSED: bool = platform::IS_Y_AXIS_REVERSED;\n\n/// Allow control of gamepad's force feedback.\n#[derive(Debug)]\npub struct FfDevice {\n    inner: platform::FfDevice,\n}\n\nimpl FfDevice {\n    /// Sets magnitude for strong and weak ff motors.\n    pub fn set_ff_state(&mut self, strong: u16, weak: u16, min_duration: Duration) {\n        self.inner.set_ff_state(strong, weak, min_duration)\n    }\n}\n\n/// Holds information about gamepad event.\n#[derive(Copy, Clone, PartialEq, Eq, Debug)]\n#[non_exhaustive]\npub struct Event {\n    /// Id of gamepad.\n    pub id: usize,\n    /// Event's data.\n    pub event: EventType,\n    /// Time when event was emitted.\n    pub time: SystemTime,\n}\n\nimpl Event {\n    /// Creates new event with current time.\n    pub fn new(id: usize, event: EventType) -> Self {\n        let time = utils::time_now();\n        Event { id, event, time }\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n/// Gamepad event.\n#[non_exhaustive]\npub enum EventType {\n    ButtonPressed(EvCode),\n    ButtonReleased(EvCode),\n    AxisValueChanged(i32, EvCode),\n    Connected,\n    Disconnected,\n}\n\n/// Holds information about expected axis range and deadzone.\n#[derive(Copy, Clone, Debug)]\npub struct AxisInfo {\n    pub min: i32,\n    pub max: i32,\n    pub deadzone: Option<u32>,\n}\n\n/// State of device's power supply.\n///\n/// Battery level is reported as integer between 0 and 100.\n///\n/// ## Example\n///\n/// ```\n/// use gilrs_core::PowerInfo;\n/// # let gilrs = gilrs_core::Gilrs::new().unwrap();\n///\n/// match gilrs.gamepad(0).map(|g| g.power_info()) {\n///     Some(PowerInfo::Discharging(lvl)) if lvl <= 10 => println!(\"Low battery level, you should \\\n///                                                           plug your gamepad\"),\n///     _ => (),\n/// };\n/// ```\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\npub enum PowerInfo {\n    /// Failed to determine power status.\n    Unknown,\n    /// Device doesn't have battery.\n    Wired,\n    /// Device is running on the battery.\n    Discharging(u8),\n    /// Battery is charging.\n    Charging(u8),\n    /// Battery is charged.\n    Charged,\n}\n\n/// Struct used to manage gamepads and retrieve events.\n#[derive(Debug)]\npub struct Gilrs {\n    inner: platform::Gilrs,\n}\n\nimpl Gilrs {\n    pub fn new() -> Result<Self, Error> {\n        let inner = platform::Gilrs::new().map_err(|e| match e {\n            PlatformError::NotImplemented(inner) => Error::NotImplemented(Gilrs { inner }),\n            PlatformError::Other(e) => Error::Other(e),\n        })?;\n\n        Ok(Gilrs { inner })\n    }\n\n    /// Returns oldest event or `None` if all events were processed.\n    pub fn next_event(&mut self) -> Option<Event> {\n        self.inner.next_event()\n    }\n\n    /// Returns oldest event, waiting for new event if necessary.\n    pub fn next_event_blocking(&mut self, timeout: Option<Duration>) -> Option<Event> {\n        self.inner.next_event_blocking(timeout)\n    }\n\n    /// Borrows `Gamepad` or return `None` if index is invalid. Returned gamepad may be disconnected.\n    pub fn gamepad(&self, id: usize) -> Option<&Gamepad> {\n        unsafe {\n            let gp: Option<&platform::Gamepad> = self.inner.gamepad(id);\n\n            gp.map(|gp| &*(gp as *const _ as *const Gamepad))\n        }\n    }\n\n    /// Returns id greater than id of last connected gamepad. The returned value is only hint\n    /// and may be much larger than number of observed gamepads. For example, it may return maximum\n    /// number of connected gamepads on platforms when this limit is small.\n    ///\n    /// `gamepad(id)` should return `Some` if using id that is smaller than value returned from this\n    /// function.\n    pub fn last_gamepad_hint(&self) -> usize {\n        self.inner.last_gamepad_hint()\n    }\n}\n\n/// Provides information about gamepad.\n#[derive(Debug)]\n#[repr(transparent)]\npub struct Gamepad {\n    inner: platform::Gamepad,\n}\n\nimpl Gamepad {\n    /// Returns name of gamepad.\n    pub fn name(&self) -> &str {\n        self.inner.name()\n    }\n\n    /// Returns true if gamepad is connected.\n    pub fn is_connected(&self) -> bool {\n        self.inner.is_connected()\n    }\n\n    /// Returns UUID that represents gamepad model.\n    ///\n    /// Returned UUID should be the same as SLD2 uses. If platform does not provide any method to\n    /// distinguish between gamepad models, nil UUID is returned.\n    ///\n    /// It is recommended to process with the [UUID crate](https://crates.io/crates/uuid).\n    /// Use `Uuid::from_bytes` method to create a `Uuid` from the returned bytes.\n    pub fn uuid(&self) -> [u8; 16] {\n        *self.inner.uuid().as_bytes()\n    }\n\n    /// Returns the vendor ID, as assigned by the USB-IF, when available.\n    pub fn vendor_id(&self) -> Option<u16> {\n        self.inner.vendor_id()\n    }\n\n    /// Returns the product ID, as assigned by the vendor, when available.\n    pub fn product_id(&self) -> Option<u16> {\n        self.inner.product_id()\n    }\n\n    /// Returns device's power supply state.\n    pub fn power_info(&self) -> PowerInfo {\n        self.inner.power_info()\n    }\n\n    /// Returns true if force feedback is supported by device,\n    pub fn is_ff_supported(&self) -> bool {\n        self.inner.is_ff_supported()\n    }\n\n    /// Creates `FfDevice` corresponding to this gamepad.\n    pub fn ff_device(&self) -> Option<FfDevice> {\n        self.inner.ff_device().map(|inner| FfDevice { inner })\n    }\n\n    /// Returns slice with EvCodes that may appear in button related events.\n    pub fn buttons(&self) -> &[EvCode] {\n        unsafe {\n            let bt: &[platform::EvCode] = self.inner.buttons();\n\n            &*(bt as *const _ as *const [EvCode])\n        }\n    }\n\n    /// Returns slice with EvCodes that may appear in axis related events.\n    pub fn axes(&self) -> &[EvCode] {\n        unsafe {\n            let ax: &[platform::EvCode] = self.inner.axes();\n\n            &*(ax as *const _ as *const [EvCode])\n        }\n    }\n\n    /// Returns information about a specific axis. `None` may be returned if a device doesn't have an axis\n    /// with provided `EvCode`.\n    pub fn axis_info(&self, nec: EvCode) -> Option<&AxisInfo> {\n        self.inner.axis_info(nec.0)\n    }\n}\n\n#[cfg(feature = \"serde-serialize\")]\nuse serde::{Deserialize, Serialize};\n\n/// Platform specific representation of axis or button.\n#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]\n#[cfg_attr(feature = \"serde-serialize\", derive(Serialize, Deserialize))]\n#[repr(transparent)]\npub struct EvCode(platform::EvCode);\n\nimpl EvCode {\n    pub fn into_u32(self) -> u32 {\n        self.0.into_u32()\n    }\n}\n\nimpl Display for EvCode {\n    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {\n        self.0.fmt(f)\n    }\n}\n\n/// Error type which can be returned when creating `Gilrs`.\n///\n/// Private version of `Error` that use `platform::Gilrs`.\n#[derive(Debug)]\nenum PlatformError {\n    /// Gilrs does not support the current platform, but you can use dummy context from this error if\n    /// gamepad input is not essential.\n    #[allow(dead_code)]\n    NotImplemented(platform::Gilrs),\n    /// Platform specific error.\n    #[allow(dead_code)]\n    Other(Box<dyn error::Error + Send + Sync>),\n}\n\nimpl Display for PlatformError {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match *self {\n            PlatformError::NotImplemented(_) => {\n                f.write_str(\"Gilrs does not support current platform.\")\n            }\n            PlatformError::Other(ref e) => e.fmt(f),\n        }\n    }\n}\n\nimpl error::Error for PlatformError {\n    fn source(&self) -> Option<&(dyn error::Error + 'static)> {\n        match self {\n            PlatformError::Other(e) => Some(e.as_ref()),\n            _ => None,\n        }\n    }\n}\n\n/// Error type which can be returned when creating `Gilrs`.\n#[non_exhaustive]\n#[derive(Debug)]\npub enum Error {\n    /// Gilrs does not support current platform, but you can use dummy context from this error if\n    /// gamepad input is not essential.\n    NotImplemented(Gilrs),\n    /// Platform specific error.\n    Other(Box<dyn error::Error + Send + Sync + 'static>),\n}\n\nimpl Display for Error {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match *self {\n            Error::NotImplemented(_) => f.write_str(\"Gilrs does not support current platform.\"),\n            Error::Other(ref e) => e.fmt(f),\n        }\n    }\n}\n\nimpl error::Error for Error {\n    fn source(&self) -> Option<&(dyn error::Error + 'static)> {\n        match self {\n            Error::Other(e) => Some(e.as_ref()),\n            _ => None,\n        }\n    }\n}\n\n/// Provides the most common mappings of physical location of gamepad elements to their EvCodes.\n/// Some (or most) gamepads may use different mappings.\npub mod native_ev_codes {\n    use super::EvCode;\n    use crate::platform::native_ev_codes as nec;\n\n    pub const AXIS_LSTICKX: EvCode = EvCode(nec::AXIS_LSTICKX);\n    pub const AXIS_LSTICKY: EvCode = EvCode(nec::AXIS_LSTICKY);\n    pub const AXIS_LEFTZ: EvCode = EvCode(nec::AXIS_LEFTZ);\n    pub const AXIS_RSTICKX: EvCode = EvCode(nec::AXIS_RSTICKX);\n    pub const AXIS_RSTICKY: EvCode = EvCode(nec::AXIS_RSTICKY);\n    pub const AXIS_RIGHTZ: EvCode = EvCode(nec::AXIS_RIGHTZ);\n    pub const AXIS_DPADX: EvCode = EvCode(nec::AXIS_DPADX);\n    pub const AXIS_DPADY: EvCode = EvCode(nec::AXIS_DPADY);\n    pub const AXIS_RT: EvCode = EvCode(nec::AXIS_RT);\n    pub const AXIS_LT: EvCode = EvCode(nec::AXIS_LT);\n    pub const AXIS_RT2: EvCode = EvCode(nec::AXIS_RT2);\n    pub const AXIS_LT2: EvCode = EvCode(nec::AXIS_LT2);\n\n    pub const BTN_SOUTH: EvCode = EvCode(nec::BTN_SOUTH);\n    pub const BTN_EAST: EvCode = EvCode(nec::BTN_EAST);\n    pub const BTN_C: EvCode = EvCode(nec::BTN_C);\n    pub const BTN_NORTH: EvCode = EvCode(nec::BTN_NORTH);\n    pub const BTN_WEST: EvCode = EvCode(nec::BTN_WEST);\n    pub const BTN_Z: EvCode = EvCode(nec::BTN_Z);\n    pub const BTN_LT: EvCode = EvCode(nec::BTN_LT);\n    pub const BTN_RT: EvCode = EvCode(nec::BTN_RT);\n    pub const BTN_LT2: EvCode = EvCode(nec::BTN_LT2);\n    pub const BTN_RT2: EvCode = EvCode(nec::BTN_RT2);\n    pub const BTN_SELECT: EvCode = EvCode(nec::BTN_SELECT);\n    pub const BTN_START: EvCode = EvCode(nec::BTN_START);\n    pub const BTN_MODE: EvCode = EvCode(nec::BTN_MODE);\n    pub const BTN_LTHUMB: EvCode = EvCode(nec::BTN_LTHUMB);\n    pub const BTN_RTHUMB: EvCode = EvCode(nec::BTN_RTHUMB);\n\n    pub const BTN_DPAD_UP: EvCode = EvCode(nec::BTN_DPAD_UP);\n    pub const BTN_DPAD_DOWN: EvCode = EvCode(nec::BTN_DPAD_DOWN);\n    pub const BTN_DPAD_LEFT: EvCode = EvCode(nec::BTN_DPAD_LEFT);\n    pub const BTN_DPAD_RIGHT: EvCode = EvCode(nec::BTN_DPAD_RIGHT);\n}\n"
  },
  {
    "path": "gilrs-core/src/platform/default/ff.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\n\nuse std::time::Duration;\n\n#[derive(Debug)]\n/// Represents gamepad. Reexported as FfDevice\npub struct Device;\n\nimpl Device {\n    /// Sets magnitude for strong and weak ff motors.\n    pub fn set_ff_state(&mut self, strong: u16, weak: u16, min_duration: Duration) {}\n}\n"
  },
  {
    "path": "gilrs-core/src/platform/default/gamepad.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\n#![allow(unused_variables)]\n\nuse super::FfDevice;\nuse crate::{AxisInfo, Event, PlatformError, PowerInfo};\nuse uuid::Uuid;\n\nuse std::fmt::{Display, Formatter, Result as FmtResult};\nuse std::time::Duration;\n\n#[derive(Debug)]\npub struct Gilrs {}\n\nimpl Gilrs {\n    pub(crate) fn new() -> Result<Self, PlatformError> {\n        Err(PlatformError::NotImplemented(Gilrs {}))\n    }\n\n    pub(crate) fn next_event(&mut self) -> Option<Event> {\n        None\n    }\n\n    pub(crate) fn next_event_blocking(&mut self, timeout: Option<Duration>) -> Option<Event> {\n        None\n    }\n\n    pub fn gamepad(&self, id: usize) -> Option<&Gamepad> {\n        None\n    }\n\n    /// Returns index greater than index of last connected gamepad.\n    pub fn last_gamepad_hint(&self) -> usize {\n        0\n    }\n}\n\n#[derive(Debug)]\npub struct Gamepad {\n    _priv: u8, // required for `#[repr(transparent)]`\n}\n\nimpl Gamepad {\n    pub fn name(&self) -> &str {\n        \"\"\n    }\n\n    pub fn uuid(&self) -> Uuid {\n        Uuid::nil()\n    }\n\n    pub fn vendor_id(&self) -> Option<u16> {\n        None\n    }\n\n    pub fn product_id(&self) -> Option<u16> {\n        None\n    }\n\n    pub fn power_info(&self) -> PowerInfo {\n        PowerInfo::Unknown\n    }\n\n    pub fn is_ff_supported(&self) -> bool {\n        false\n    }\n\n    /// Creates Ffdevice corresponding to this gamepad.\n    pub fn ff_device(&self) -> Option<FfDevice> {\n        Some(FfDevice)\n    }\n\n    pub fn buttons(&self) -> &[EvCode] {\n        &[]\n    }\n\n    pub fn axes(&self) -> &[EvCode] {\n        &[]\n    }\n\n    pub(crate) fn axis_info(&self, nec: EvCode) -> Option<&AxisInfo> {\n        None\n    }\n\n    pub fn is_connected(&self) -> bool {\n        false\n    }\n}\n\n#[cfg(feature = \"serde-serialize\")]\nuse serde::{Deserialize, Serialize};\n\n#[cfg_attr(feature = \"serde-serialize\", derive(Serialize, Deserialize))]\n#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]\npub struct EvCode(u16);\n\nimpl EvCode {\n    pub fn into_u32(self) -> u32 {\n        self.0 as u32\n    }\n}\n\nimpl Display for EvCode {\n    fn fmt(&self, f: &mut Formatter) -> FmtResult {\n        self.0.fmt(f)\n    }\n}\n\npub mod native_ev_codes {\n    use super::EvCode;\n\n    pub const AXIS_LSTICKX: EvCode = EvCode(0);\n    pub const AXIS_LSTICKY: EvCode = EvCode(1);\n    pub const AXIS_LEFTZ: EvCode = EvCode(2);\n    pub const AXIS_RSTICKX: EvCode = EvCode(3);\n    pub const AXIS_RSTICKY: EvCode = EvCode(4);\n    pub const AXIS_RIGHTZ: EvCode = EvCode(5);\n    pub const AXIS_DPADX: EvCode = EvCode(6);\n    pub const AXIS_DPADY: EvCode = EvCode(7);\n    pub const AXIS_RT: EvCode = EvCode(8);\n    pub const AXIS_LT: EvCode = EvCode(9);\n    pub const AXIS_RT2: EvCode = EvCode(10);\n    pub const AXIS_LT2: EvCode = EvCode(11);\n\n    pub const BTN_SOUTH: EvCode = EvCode(12);\n    pub const BTN_EAST: EvCode = EvCode(13);\n    pub const BTN_C: EvCode = EvCode(14);\n    pub const BTN_NORTH: EvCode = EvCode(15);\n    pub const BTN_WEST: EvCode = EvCode(16);\n    pub const BTN_Z: EvCode = EvCode(17);\n    pub const BTN_LT: EvCode = EvCode(18);\n    pub const BTN_RT: EvCode = EvCode(19);\n    pub const BTN_LT2: EvCode = EvCode(20);\n    pub const BTN_RT2: EvCode = EvCode(21);\n    pub const BTN_SELECT: EvCode = EvCode(22);\n    pub const BTN_START: EvCode = EvCode(23);\n    pub const BTN_MODE: EvCode = EvCode(24);\n    pub const BTN_LTHUMB: EvCode = EvCode(25);\n    pub const BTN_RTHUMB: EvCode = EvCode(26);\n\n    pub const BTN_DPAD_UP: EvCode = EvCode(27);\n    pub const BTN_DPAD_DOWN: EvCode = EvCode(28);\n    pub const BTN_DPAD_LEFT: EvCode = EvCode(29);\n    pub const BTN_DPAD_RIGHT: EvCode = EvCode(30);\n}\n"
  },
  {
    "path": "gilrs-core/src/platform/default/mod.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\nmod ff;\nmod gamepad;\n\npub use self::ff::Device as FfDevice;\npub use self::gamepad::{native_ev_codes, EvCode, Gamepad, Gilrs};\n\n// True, if Y axis of sticks points downwards.\npub const IS_Y_AXIS_REVERSED: bool = false;\n"
  },
  {
    "path": "gilrs-core/src/platform/linux/ff.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\n\nuse std::fs::File;\nuse std::io::{Error as IoError, ErrorKind, Result as IoResult, Write};\nuse std::os::unix::io::AsRawFd;\nuse std::{mem, slice};\n\nuse super::ioctl::{self, ff_effect, ff_replay, ff_rumble_effect, input_event};\nuse nix::errno::Errno;\nuse std::time::Duration;\n\n#[derive(Debug)]\npub struct Device {\n    effect: i16,\n    file: File,\n}\n\nimpl Device {\n    pub(crate) fn new(path: &str) -> IoResult<Self> {\n        let file = File::create(path)?;\n        let mut effect = ff_effect {\n            type_: FF_RUMBLE,\n            id: -1,\n            direction: 0,\n            trigger: Default::default(),\n            replay: Default::default(),\n            u: Default::default(),\n        };\n\n        #[allow(clippy::unnecessary_mut_passed)]\n        let res = unsafe { ioctl::eviocsff(file.as_raw_fd(), &mut effect) };\n\n        if res.is_err() {\n            Err(IoError::new(ErrorKind::Other, \"Failed to create effect\"))\n        } else {\n            Ok(Device {\n                effect: effect.id,\n                file,\n            })\n        }\n    }\n\n    pub fn set_ff_state(&mut self, strong: u16, weak: u16, min_duration: Duration) {\n        let duration = min_duration.as_secs() * 1000 + u64::from(min_duration.subsec_millis());\n        let duration = if duration > u64::from(u16::MAX) {\n            u16::MAX\n        } else {\n            duration as u16\n        };\n\n        let mut effect = ff_effect {\n            type_: FF_RUMBLE,\n            id: self.effect,\n            direction: 0,\n            trigger: Default::default(),\n            replay: ff_replay {\n                delay: 0,\n                length: duration,\n            },\n            u: Default::default(),\n        };\n\n        unsafe {\n            let rumble = &mut effect.u as *mut _ as *mut ff_rumble_effect;\n            (*rumble).strong_magnitude = strong;\n            (*rumble).weak_magnitude = weak;\n\n            if let Err(err) = ioctl::eviocsff(self.file.as_raw_fd(), &effect) {\n                error!(\n                    \"Failed to modify effect of gamepad {:?}, error: {}\",\n                    self.file, err\n                );\n\n                return;\n            }\n        };\n\n        let time = libc::timeval {\n            tv_sec: 0,\n            tv_usec: 0,\n        };\n        let ev = input_event {\n            type_: EV_FF,\n            code: self.effect as u16,\n            value: 1,\n            time,\n        };\n\n        let size = mem::size_of::<input_event>();\n        let s = unsafe { slice::from_raw_parts(&ev as *const _ as *const u8, size) };\n\n        match self.file.write(s) {\n            Ok(s) if s == size => (),\n            Ok(_) => unreachable!(),\n            Err(e) => error!(\"Failed to set ff state: {}\", e),\n        }\n    }\n}\n\nimpl Drop for Device {\n    fn drop(&mut self) {\n        #[cfg(target_os = \"linux\")]\n        let effect = self.effect as ::libc::c_ulong;\n        #[cfg(not(target_os = \"linux\"))]\n        let effect = self.effect as ::libc::c_int;\n\n        if let Err(err) = unsafe { ioctl::eviocrmff(self.file.as_raw_fd(), effect) } {\n            if err != Errno::ENODEV {\n                error!(\n                    \"Failed to remove effect of gamepad {:?}: {}\",\n                    self.file, err\n                )\n            }\n        };\n    }\n}\n\nconst EV_FF: u16 = 0x15;\nconst FF_RUMBLE: u16 = 0x50;\n"
  },
  {
    "path": "gilrs-core/src/platform/linux/gamepad.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\n\nuse super::ff::Device as FfDevice;\nuse super::ioctl;\nuse super::ioctl::{input_absinfo, input_event};\nuse super::udev::*;\nuse crate::utils;\nuse crate::{AxisInfo, Event, EventType};\nuse crate::{PlatformError, PowerInfo};\n\nuse libc as c;\nuse uuid::Uuid;\nuse vec_map::VecMap;\n\nuse inotify::{EventMask, Inotify, WatchMask};\nuse nix::errno::Errno;\nuse nix::sys::epoll::{Epoll, EpollCreateFlags, EpollEvent, EpollFlags, EpollTimeout};\nuse nix::sys::eventfd::{EfdFlags, EventFd};\nuse std::collections::VecDeque;\nuse std::error;\nuse std::ffi::OsStr;\nuse std::ffi::{CStr, CString};\nuse std::fmt::{Display, Formatter, Result as FmtResult};\nuse std::fs::File;\nuse std::mem::{self, MaybeUninit};\nuse std::ops::Index;\nuse std::os::raw::c_char;\nuse std::os::unix::ffi::OsStrExt;\nuse std::os::unix::io::{BorrowedFd, RawFd};\nuse std::path::{Path, PathBuf};\nuse std::str;\nuse std::sync::mpsc;\nuse std::sync::mpsc::{Receiver, Sender};\nuse std::time::{Duration, SystemTime, UNIX_EPOCH};\n\nconst HOTPLUG_DATA: u64 = u64::MAX;\n\n#[derive(Debug)]\npub struct Gilrs {\n    gamepads: Vec<Gamepad>,\n    epoll: Epoll,\n    hotplug_rx: Receiver<HotplugEvent>,\n    to_check: VecDeque<usize>,\n    discovery_backend: DiscoveryBackend,\n}\n\n#[derive(Debug, Clone, Copy)]\nenum DiscoveryBackend {\n    Udev,\n    Inotify,\n}\n\nconst INPUT_DIR_PATH: &str = \"/dev/input\";\n\nimpl Gilrs {\n    pub(crate) fn new() -> Result<Self, PlatformError> {\n        let mut gamepads = Vec::new();\n        let epoll = Epoll::new(EpollCreateFlags::empty())\n            .map_err(|e| errno_to_platform_error(e, \"creating epoll fd\"))?;\n\n        let mut hotplug_event = EventFd::from_value_and_flags(1, EfdFlags::EFD_NONBLOCK)\n            .map_err(|e| errno_to_platform_error(e, \"creating eventfd\"))?;\n        epoll\n            .add(\n                &hotplug_event,\n                EpollEvent::new(EpollFlags::EPOLLIN | EpollFlags::EPOLLET, HOTPLUG_DATA),\n            )\n            .map_err(|e| errno_to_platform_error(e, \"adding evevntfd do epoll\"))?;\n\n        if Path::new(\"/.flatpak-info\").exists() || std::env::var(\"GILRS_DISABLE_UDEV\").is_ok() {\n            log::debug!(\"Looks like we're in an environment without udev. Falling back to inotify\");\n            let (hotplug_tx, hotplug_rx) = mpsc::channel();\n            let mut inotify = Inotify::init().map_err(|err| PlatformError::Other(Box::new(err)))?;\n            let input_dir = Path::new(INPUT_DIR_PATH);\n            inotify\n                .watches()\n                .add(\n                    input_dir,\n                    WatchMask::CREATE | WatchMask::DELETE | WatchMask::MOVE | WatchMask::ATTRIB,\n                )\n                .map_err(|err| PlatformError::Other(Box::new(err)))?;\n\n            for entry in input_dir\n                .read_dir()\n                .map_err(|err| PlatformError::Other(Box::new(err)))?\n                .flatten()\n            {\n                let file_name = match entry.file_name().into_string() {\n                    Ok(file_name) => file_name,\n                    Err(_) => continue,\n                };\n                let (gamepad_path, syspath) = match get_gamepad_path(&file_name) {\n                    Some((gamepad_path, syspath)) => (gamepad_path, syspath),\n                    None => continue,\n                };\n                let devpath = CString::new(gamepad_path.to_str().unwrap()).unwrap();\n                if let Some(gamepad) = Gamepad::open(&devpath, &syspath, DiscoveryBackend::Inotify)\n                {\n                    let idx = gamepads.len();\n                    gamepad\n                        .register_fd(&epoll, idx as u64)\n                        .map_err(|e| errno_to_platform_error(e, \"registering gamepad in epoll\"))?;\n                    gamepads.push(gamepad);\n                }\n            }\n\n            std::thread::Builder::new()\n                .name(\"gilrs\".to_owned())\n                .spawn(move || {\n                    let mut buffer = [0u8; 1024];\n                    debug!(\"Started gilrs inotify thread\");\n                    loop {\n                        let events = match inotify.read_events_blocking(&mut buffer) {\n                            Ok(events) => events,\n                            Err(err) => {\n                                error!(\"Failed to check for changes to joysticks: {err}\");\n                                return;\n                            }\n                        };\n                        for event in events {\n                            if !handle_inotify(&hotplug_tx, event, &mut hotplug_event) {\n                                return;\n                            }\n                        }\n                    }\n                })\n                .expect(\"failed to spawn thread\");\n            return Ok(Gilrs {\n                gamepads,\n                epoll,\n                hotplug_rx,\n                to_check: VecDeque::new(),\n                discovery_backend: DiscoveryBackend::Inotify,\n            });\n        }\n        let udev = match Udev::new() {\n            Some(udev) => udev,\n            None => {\n                return Err(PlatformError::Other(Box::new(Error::UdevCtx)));\n            }\n        };\n        let en = match udev.enumerate() {\n            Some(en) => en,\n            None => {\n                return Err(PlatformError::Other(Box::new(Error::UdevEnumerate)));\n            }\n        };\n\n        unsafe { en.add_match_property(cstr_new(b\"ID_INPUT_JOYSTICK\\0\"), cstr_new(b\"1\\0\")) }\n        unsafe { en.add_match_subsystem(cstr_new(b\"input\\0\")) }\n        en.scan_devices();\n\n        for dev in en.iter() {\n            if let Some(dev) = Device::from_syspath(&udev, &dev) {\n                let devpath = match dev.devnode() {\n                    Some(devpath) => devpath,\n                    None => continue,\n                };\n                let syspath = Path::new(OsStr::from_bytes(dev.syspath().to_bytes()));\n                if let Some(gamepad) = Gamepad::open(devpath, syspath, DiscoveryBackend::Udev) {\n                    let idx = gamepads.len();\n                    gamepad\n                        .register_fd(&epoll, idx as u64)\n                        .map_err(|e| errno_to_platform_error(e, \"registering gamepad in epoll\"))?;\n                    gamepads.push(gamepad);\n                }\n            }\n        }\n\n        let (hotplug_tx, hotplug_rx) = mpsc::channel();\n        std::thread::Builder::new()\n            .name(\"gilrs\".to_owned())\n            .spawn(move || {\n                let udev = match Udev::new() {\n                    Some(udev) => udev,\n                    None => {\n                        error!(\"Failed to create udev for hot plug thread!\");\n                        return;\n                    }\n                };\n\n                let monitor = match Monitor::new(&udev) {\n                    Some(m) => m,\n                    None => {\n                        error!(\"Failed to create udev monitor for hot plug thread!\");\n                        return;\n                    }\n                };\n\n                handle_hotplug(hotplug_tx, monitor, hotplug_event)\n            })\n            .expect(\"failed to spawn thread\");\n\n        Ok(Gilrs {\n            gamepads,\n            epoll,\n            hotplug_rx,\n            to_check: VecDeque::new(),\n            discovery_backend: DiscoveryBackend::Udev,\n        })\n    }\n\n    pub(crate) fn next_event(&mut self) -> Option<Event> {\n        self.next_event_impl(Some(Duration::new(0, 0)))\n    }\n\n    pub(crate) fn next_event_blocking(&mut self, timeout: Option<Duration>) -> Option<Event> {\n        self.next_event_impl(timeout)\n    }\n\n    fn next_event_impl(&mut self, timeout: Option<Duration>) -> Option<Event> {\n        let mut check_hotplug = false;\n\n        if self.to_check.is_empty() {\n            let mut events = [EpollEvent::new(EpollFlags::empty(), 0); 16];\n            let timeout = if let Some(timeout) = timeout {\n                EpollTimeout::try_from(timeout).expect(\"timeout too large\")\n            } else {\n                EpollTimeout::NONE\n            };\n\n            let n = match self.epoll.wait(&mut events, timeout) {\n                Ok(n) => n,\n                Err(e) => {\n                    error!(\"epoll failed: {}\", e);\n                    return None;\n                }\n            };\n\n            if n == 0 {\n                return None;\n            }\n\n            for event in events {\n                if event.events().contains(EpollFlags::EPOLLIN) {\n                    if event.data() == HOTPLUG_DATA {\n                        check_hotplug = true;\n                    } else {\n                        self.to_check.push_back(event.data() as usize);\n                    }\n                }\n            }\n        }\n\n        if check_hotplug {\n            if let Some(event) = self.handle_hotplug() {\n                return Some(event);\n            }\n        }\n\n        while let Some(idx) = self.to_check.front().copied() {\n            let gamepad = match self.gamepads.get_mut(idx) {\n                Some(gp) => gp,\n                None => {\n                    warn!(\"Somehow got invalid index from event\");\n                    self.to_check.pop_front();\n                    return None;\n                }\n            };\n\n            if !gamepad.is_connected {\n                self.to_check.pop_front();\n                continue;\n            }\n\n            match gamepad.event() {\n                Some((event, time)) => {\n                    return Some(Event {\n                        id: idx,\n                        event,\n                        time,\n                    });\n                }\n                None => {\n                    self.to_check.pop_front();\n                    continue;\n                }\n            };\n        }\n\n        None\n    }\n\n    pub fn gamepad(&self, id: usize) -> Option<&Gamepad> {\n        self.gamepads.get(id)\n    }\n\n    pub fn last_gamepad_hint(&self) -> usize {\n        self.gamepads.len()\n    }\n\n    fn handle_hotplug(&mut self) -> Option<Event> {\n        while let Ok(event) = self.hotplug_rx.try_recv() {\n            match event {\n                HotplugEvent::New { devpath, syspath } => {\n                    // We already know this gamepad, ignore it:\n                    let gamepad_path_str = devpath.clone().to_string_lossy().into_owned();\n                    if self\n                        .gamepads\n                        .iter()\n                        .any(|gamepad| gamepad.devpath == gamepad_path_str && gamepad.is_connected)\n                    {\n                        continue;\n                    }\n                    if let Some(gamepad) = Gamepad::open(&devpath, &syspath, self.discovery_backend)\n                    {\n                        return if let Some(id) = self\n                            .gamepads\n                            .iter()\n                            .position(|gp| gp.uuid() == gamepad.uuid && !gp.is_connected)\n                        {\n                            if let Err(e) = gamepad.register_fd(&self.epoll, id as u64) {\n                                error!(\"Failed to add gamepad to epoll: {}\", e);\n                            }\n                            self.gamepads[id] = gamepad;\n                            Some(Event::new(id, EventType::Connected))\n                        } else {\n                            if let Err(e) =\n                                gamepad.register_fd(&self.epoll, self.gamepads.len() as u64)\n                            {\n                                error!(\"Failed to add gamepad to epoll: {}\", e);\n                            }\n                            self.gamepads.push(gamepad);\n                            Some(Event::new(self.gamepads.len() - 1, EventType::Connected))\n                        };\n                    }\n                }\n                HotplugEvent::Removed(devpath) => {\n                    if let Some(id) = self\n                        .gamepads\n                        .iter()\n                        .position(|gp| devpath == gp.devpath && gp.is_connected)\n                    {\n                        let gamepad_fd = unsafe { BorrowedFd::borrow_raw(self.gamepads[id].fd) };\n                        if let Err(e) = self.epoll.delete(gamepad_fd) {\n                            error!(\"Failed to remove disconnected gamepad from epoll: {}\", e);\n                        }\n\n                        self.gamepads[id].disconnect();\n                        return Some(Event::new(id, EventType::Disconnected));\n                    } else {\n                        debug!(\"Could not find disconnected gamepad {devpath:?}\");\n                    }\n                }\n            }\n        }\n\n        None\n    }\n}\n\nenum HotplugEvent {\n    New { devpath: CString, syspath: PathBuf },\n    Removed(String),\n}\n\nfn handle_inotify(\n    sender: &Sender<HotplugEvent>,\n    event: inotify::Event<&std::ffi::OsStr>,\n    event_fd: &mut EventFd,\n) -> bool {\n    let name = match event.name.and_then(|name| name.to_str()) {\n        Some(name) => name,\n        None => return true,\n    };\n    let (gamepad_path, syspath) = match get_gamepad_path(name) {\n        Some((gamepad_path, syspath)) => (gamepad_path, syspath),\n        None => return true,\n    };\n\n    let mut sent = false;\n\n    if !(event.mask & (EventMask::CREATE | EventMask::MOVED_TO | EventMask::ATTRIB)).is_empty() {\n        if sender\n            .send(HotplugEvent::New {\n                devpath: CString::new(gamepad_path.to_str().unwrap()).unwrap(),\n                syspath,\n            })\n            .is_err()\n        {\n            debug!(\"All receivers dropped, ending hot plug loop.\");\n            return false;\n        }\n        sent = true;\n    } else if !(event.mask & (EventMask::DELETE | EventMask::MOVED_FROM)).is_empty() {\n        if sender\n            .send(HotplugEvent::Removed(\n                gamepad_path.to_string_lossy().to_string(),\n            ))\n            .is_err()\n        {\n            debug!(\"All receivers dropped, ending hot plug loop.\");\n            return false;\n        }\n        sent = true;\n    }\n    if sent {\n        if let Err(e) = event_fd.write(0u64) {\n            error!(\n                \"Failed to notify other thread about new hotplug events: {}\",\n                e\n            );\n        }\n    }\n    true\n}\n\nfn get_gamepad_path(name: &str) -> Option<(PathBuf, PathBuf)> {\n    let event_id =  name.strip_prefix(\"event\")?;\n\n    if event_id.is_empty()\n        || event_id\n            .chars()\n            .any(|character| !character.is_ascii_digit())\n    {\n        return None;\n    }\n\n    let gamepad_path = Path::new(INPUT_DIR_PATH).join(name);\n    let syspath = Path::new(\"/sys/class/input/\").join(name);\n    Some((gamepad_path, syspath))\n}\n\nfn handle_hotplug(sender: Sender<HotplugEvent>, monitor: Monitor, event: EventFd) {\n    loop {\n        if !monitor.wait_hotplug_available() {\n            continue;\n        }\n\n        let dev = monitor.device();\n\n        unsafe {\n            if let Some(val) = dev.property_value(cstr_new(b\"ID_INPUT_JOYSTICK\\0\")) {\n                if val != cstr_new(b\"1\\0\") {\n                    continue;\n                }\n            } else {\n                continue;\n            }\n\n            let action = match dev.action() {\n                Some(a) => a,\n                None => continue,\n            };\n\n            let mut sent = false;\n\n            if action == cstr_new(b\"add\\0\") {\n                if let Some(devpath) = dev.devnode() {\n                    let syspath = Path::new(OsStr::from_bytes(dev.syspath().to_bytes()));\n                    if sender\n                        .send(HotplugEvent::New {\n                            devpath: devpath.into(),\n                            syspath: syspath.to_path_buf(),\n                        })\n                        .is_err()\n                    {\n                        debug!(\"All receivers dropped, ending hot plug loop.\");\n                        break;\n                    }\n                    sent = true;\n                }\n            } else if action == cstr_new(b\"remove\\0\") {\n                if let Some(devnode) = dev.devnode() {\n                    if let Ok(str) = devnode.to_str() {\n                        if sender.send(HotplugEvent::Removed(str.to_owned())).is_err() {\n                            debug!(\"All receivers dropped, ending hot plug loop.\");\n                            break;\n                        }\n                        sent = true;\n                    } else {\n                        warn!(\"Received event with devnode that is not valid utf8: {devnode:?}\")\n                    }\n                }\n            }\n\n            if sent {\n                if let Err(e) = event.write(0) {\n                    error!(\n                        \"Failed to notify other thread about new hotplug events: {}\",\n                        e\n                    );\n                }\n            }\n        }\n    }\n}\n\n#[derive(Debug, Clone)]\nstruct AxesInfo {\n    info: VecMap<AxisInfo>,\n}\n\nimpl AxesInfo {\n    fn new(fd: i32) -> Self {\n        let mut map = VecMap::new();\n\n        unsafe {\n            let mut abs_bits = [0u8; (ABS_MAX / 8) as usize + 1];\n            ioctl::eviocgbit(\n                fd,\n                u32::from(EV_ABS),\n                abs_bits.len() as i32,\n                abs_bits.as_mut_ptr(),\n            );\n\n            for axis in Gamepad::find_axes(&abs_bits) {\n                let mut info = input_absinfo::default();\n                ioctl::eviocgabs(fd, u32::from(axis.code), &mut info);\n                map.insert(\n                    axis.code as usize,\n                    AxisInfo {\n                        min: info.minimum,\n                        max: info.maximum,\n                        deadzone: Some(info.flat as u32),\n                    },\n                );\n            }\n        }\n\n        AxesInfo { info: map }\n    }\n}\n\nimpl Index<u16> for AxesInfo {\n    type Output = AxisInfo;\n\n    fn index(&self, i: u16) -> &Self::Output {\n        &self.info[i as usize]\n    }\n}\n\n#[derive(Debug)]\npub struct Gamepad {\n    fd: RawFd,\n    axes_info: AxesInfo,\n    ff_supported: bool,\n    devpath: String,\n    name: String,\n    uuid: Uuid,\n    vendor_id: u16,\n    product_id: u16,\n    bt_capacity_fd: RawFd,\n    bt_status_fd: RawFd,\n    axes_values: VecMap<i32>,\n    buttons_values: VecMap<bool>,\n    events: Vec<input_event>,\n    axes: Vec<EvCode>,\n    buttons: Vec<EvCode>,\n    is_connected: bool,\n}\n\nimpl Gamepad {\n    fn open(path: &CStr, syspath: &Path, discovery_backend: DiscoveryBackend) -> Option<Gamepad> {\n        if unsafe { !c::strstr(path.as_ptr(), c\"js\".as_ptr() as *const c_char).is_null() } {\n            trace!(\"Device {:?} is js interface, ignoring.\", path);\n            return None;\n        }\n\n        let fd = unsafe { c::open(path.as_ptr(), c::O_RDWR | c::O_NONBLOCK) };\n        if fd < 0 {\n            log!(\n                match discovery_backend {\n                    DiscoveryBackend::Inotify => log::Level::Debug,\n                    _ => log::Level::Error,\n                },\n                \"Failed to open {:?}\",\n                path\n            );\n            return None;\n        }\n\n        let input_id = match Self::get_input_id(fd) {\n            Some(input_id) => input_id,\n            None => {\n                error!(\"Failed to get id of device {:?}\", path);\n                unsafe {\n                    c::close(fd);\n                }\n                return None;\n            }\n        };\n\n        let name = Self::get_name(fd).unwrap_or_else(|| {\n            error!(\"Failed to get name of device {:?}\", path);\n            \"Unknown\".into()\n        });\n\n        let axesi = AxesInfo::new(fd);\n        let ff_supported = Self::test_ff(fd);\n        let (cap, status) = Self::battery_fd(syspath);\n\n        let mut gamepad = Gamepad {\n            fd,\n            axes_info: axesi,\n            ff_supported,\n            devpath: path.to_string_lossy().into_owned(),\n            name,\n            uuid: create_uuid(input_id),\n            vendor_id: input_id.vendor,\n            product_id: input_id.product,\n            bt_capacity_fd: cap,\n            bt_status_fd: status,\n            axes_values: VecMap::new(),\n            buttons_values: VecMap::new(),\n            events: Vec::new(),\n            axes: Vec::new(),\n            buttons: Vec::new(),\n            is_connected: true,\n        };\n\n        gamepad.collect_axes_and_buttons();\n\n        if !gamepad.is_gamepad() {\n            log!(\n                match discovery_backend {\n                    DiscoveryBackend::Inotify => log::Level::Debug,\n                    _ => log::Level::Warn,\n                },\n                \"{:?} doesn't have at least 1 button and 2 axes, ignoring.\",\n                path\n            );\n            return None;\n        }\n\n        info!(\"Gamepad {} ({}) connected.\", gamepad.devpath, gamepad.name);\n        debug!(\n            \"Gamepad {}: uuid: {}, ff_supported: {}, axes: {:?}, buttons: {:?}, axes_info: {:?}\",\n            gamepad.devpath,\n            gamepad.uuid,\n            gamepad.ff_supported,\n            gamepad.axes,\n            gamepad.buttons,\n            gamepad.axes_info\n        );\n\n        Some(gamepad)\n    }\n\n    fn register_fd(&self, epoll: &Epoll, data: u64) -> Result<(), Errno> {\n        let fd = unsafe { BorrowedFd::borrow_raw(self.fd) };\n        epoll.add(fd, EpollEvent::new(EpollFlags::EPOLLIN, data))\n    }\n\n    fn collect_axes_and_buttons(&mut self) {\n        let mut key_bits = [0u8; (KEY_MAX / 8) as usize + 1];\n        let mut abs_bits = [0u8; (ABS_MAX / 8) as usize + 1];\n\n        unsafe {\n            ioctl::eviocgbit(\n                self.fd,\n                u32::from(EV_KEY),\n                key_bits.len() as i32,\n                key_bits.as_mut_ptr(),\n            );\n            ioctl::eviocgbit(\n                self.fd,\n                u32::from(EV_ABS),\n                abs_bits.len() as i32,\n                abs_bits.as_mut_ptr(),\n            );\n        }\n\n        self.buttons = Self::find_buttons(&key_bits, false);\n        self.axes = Self::find_axes(&abs_bits);\n    }\n\n    fn get_name(fd: i32) -> Option<String> {\n        unsafe {\n            let mut namebuff: [MaybeUninit<u8>; 128] = MaybeUninit::uninit().assume_init();\n            if ioctl::eviocgname(fd, &mut namebuff).is_err() {\n                None\n            } else {\n                Some(\n                    CStr::from_ptr(namebuff.as_ptr() as *const c_char)\n                        .to_string_lossy()\n                        .into_owned(),\n                )\n            }\n        }\n    }\n\n    fn get_input_id(fd: i32) -> Option<ioctl::input_id> {\n        unsafe {\n            let mut iid = MaybeUninit::<ioctl::input_id>::uninit();\n            if ioctl::eviocgid(fd, iid.as_mut_ptr()).is_err() {\n                return None;\n            }\n\n            Some(iid.assume_init())\n        }\n    }\n\n    fn test_ff(fd: i32) -> bool {\n        unsafe {\n            let mut ff_bits = [0u8; (FF_MAX / 8) as usize + 1];\n            if ioctl::eviocgbit(\n                fd,\n                u32::from(EV_FF),\n                ff_bits.len() as i32,\n                ff_bits.as_mut_ptr(),\n            ) >= 0\n            {\n                utils::test_bit(FF_SQUARE, &ff_bits)\n                    && utils::test_bit(FF_TRIANGLE, &ff_bits)\n                    && utils::test_bit(FF_SINE, &ff_bits)\n                    && utils::test_bit(FF_GAIN, &ff_bits)\n            } else {\n                false\n            }\n        }\n    }\n\n    fn is_gamepad(&self) -> bool {\n        // TODO: improve it (for example check for buttons in range)\n        !self.buttons.is_empty() && self.axes.len() >= 2\n    }\n\n    fn find_buttons(key_bits: &[u8], only_gamepad_btns: bool) -> Vec<EvCode> {\n        let mut buttons = Vec::with_capacity(16);\n\n        for bit in BTN_MISC..BTN_MOUSE {\n            if utils::test_bit(bit, key_bits) {\n                buttons.push(EvCode::new(EV_KEY, bit));\n            }\n        }\n        for bit in BTN_JOYSTICK..(key_bits.len() as u16 * 8) {\n            if utils::test_bit(bit, key_bits) {\n                buttons.push(EvCode::new(EV_KEY, bit));\n            }\n        }\n\n        if !only_gamepad_btns {\n            for bit in 0..BTN_MISC {\n                if utils::test_bit(bit, key_bits) {\n                    buttons.push(EvCode::new(EV_KEY, bit));\n                }\n            }\n            for bit in BTN_MOUSE..BTN_JOYSTICK {\n                if utils::test_bit(bit, key_bits) {\n                    buttons.push(EvCode::new(EV_KEY, bit));\n                }\n            }\n        }\n\n        buttons\n    }\n\n    fn find_axes(abs_bits: &[u8]) -> Vec<EvCode> {\n        let mut axes = Vec::with_capacity(8);\n\n        for bit in 0..(abs_bits.len() * 8) {\n            if utils::test_bit(bit as u16, abs_bits) {\n                axes.push(EvCode::new(EV_ABS, bit as u16));\n            }\n        }\n\n        axes\n    }\n\n    fn battery_fd(syspath: &Path) -> (i32, i32) {\n        use std::fs::{self};\n        use std::os::unix::io::IntoRawFd;\n\n        // Returned syspath points to <device path>/input/inputXX/eventXX. First \"device\" is\n        // symlink to inputXX, second to actual device root.\n        let syspath = syspath.join(\"device/device/power_supply\");\n        if let Ok(mut read_dir) = fs::read_dir(syspath) {\n            if let Some(Ok(bat_entry)) = read_dir.next() {\n                if let Ok(cap) = File::open(bat_entry.path().join(\"capacity\")) {\n                    if let Ok(status) = File::open(bat_entry.path().join(\"status\")) {\n                        return (cap.into_raw_fd(), status.into_raw_fd());\n                    }\n                }\n            }\n        }\n        (-1, -1)\n    }\n\n    fn event(&mut self) -> Option<(EventType, SystemTime)> {\n        let mut skip = false;\n        // Skip all unknown events and return Option on first know event or when there is no more\n        // events to read. Returning None on unknown event breaks iterators.\n        loop {\n            let event = self.next_event()?;\n\n            if skip {\n                if event.type_ == EV_SYN && event.code == SYN_REPORT {\n                    skip = false;\n                    self.compare_state();\n                }\n                continue;\n            }\n\n            let ev = match event.type_ {\n                EV_SYN if event.code == SYN_DROPPED => {\n                    skip = true;\n                    None\n                }\n                EV_KEY => {\n                    self.buttons_values\n                        .insert(event.code as usize, event.value == 1);\n                    match event.value {\n                        0 => Some(EventType::ButtonReleased(event.into())),\n                        1 => Some(EventType::ButtonPressed(event.into())),\n                        _ => None,\n                    }\n                }\n                EV_ABS => {\n                    self.axes_values.insert(event.code as usize, event.value);\n                    Some(EventType::AxisValueChanged(event.value, event.into()))\n                }\n                _ => {\n                    trace!(\"Skipping event {:?}\", event);\n                    None\n                }\n            };\n\n            if let Some(ev) = ev {\n                let dur = Duration::new(event.time.tv_sec as u64, event.time.tv_usec as u32 * 1000);\n\n                return Some((ev, UNIX_EPOCH + dur));\n            }\n        }\n    }\n\n    fn next_event(&mut self) -> Option<input_event> {\n        if !self.events.is_empty() {\n            self.events.pop()\n        } else {\n            unsafe {\n                let mut event_buf: [MaybeUninit<ioctl::input_event>; 12] =\n                    MaybeUninit::uninit().assume_init();\n                let size = mem::size_of::<ioctl::input_event>();\n                let n = c::read(\n                    self.fd,\n                    event_buf.as_mut_ptr() as *mut c::c_void,\n                    size * event_buf.len(),\n                );\n\n                if n == -1 || n == 0 {\n                    // Nothing to read (non-blocking IO)\n                    None\n                } else if n % size as isize != 0 {\n                    error!(\"Unexpected read of size {}\", n);\n                    None\n                } else {\n                    let n = n as usize / size;\n                    trace!(\"Got {} new events\", n);\n                    for ev in event_buf[1..n].iter().rev() {\n                        self.events.push(ev.assume_init());\n                    }\n\n                    Some(event_buf[0].assume_init())\n                }\n            }\n        }\n    }\n\n    fn compare_state(&mut self) {\n        let mut absinfo = input_absinfo::default();\n        for axis in self.axes.iter().cloned() {\n            let value = unsafe {\n                ioctl::eviocgabs(self.fd, u32::from(axis.code), &mut absinfo);\n                absinfo.value\n            };\n\n            if self\n                .axes_values\n                .get(axis.code as usize)\n                .cloned()\n                .unwrap_or(0)\n                != value\n            {\n                self.events.push(input_event {\n                    type_: EV_ABS,\n                    code: axis.code,\n                    value,\n                    ..Default::default()\n                });\n            }\n        }\n\n        let mut buf = [0u8; KEY_MAX as usize / 8 + 1];\n        unsafe {\n            let _ = ioctl::eviocgkey(self.fd, &mut buf);\n        }\n\n        for btn in self.buttons.iter().cloned() {\n            let val = utils::test_bit(btn.code, &buf);\n            if self\n                .buttons_values\n                .get(btn.code as usize)\n                .cloned()\n                .unwrap_or(false)\n                != val\n            {\n                self.events.push(input_event {\n                    type_: EV_KEY,\n                    code: btn.code,\n                    value: val as i32,\n                    ..Default::default()\n                });\n            }\n        }\n    }\n\n    fn disconnect(&mut self) {\n        unsafe {\n            if self.fd >= 0 {\n                c::close(self.fd);\n            }\n        }\n        self.fd = -2;\n        self.devpath.clear();\n        self.is_connected = false;\n    }\n\n    pub fn is_connected(&self) -> bool {\n        self.is_connected\n    }\n\n    pub fn power_info(&self) -> PowerInfo {\n        if self.bt_capacity_fd > -1 && self.bt_status_fd > -1 {\n            unsafe {\n                let mut buff = [0u8; 15];\n                c::lseek(self.bt_capacity_fd, 0, c::SEEK_SET);\n                c::lseek(self.bt_status_fd, 0, c::SEEK_SET);\n\n                let len = c::read(\n                    self.bt_capacity_fd,\n                    buff.as_mut_ptr() as *mut c::c_void,\n                    buff.len(),\n                );\n\n                if len > 0 {\n                    let len = len as usize;\n                    let cap = match str::from_utf8_unchecked(&buff[..(len - 1)]).parse() {\n                        Ok(cap) => cap,\n                        Err(_) => {\n                            error!(\n                                \"Failed to parse battery capacity: {}\",\n                                str::from_utf8_unchecked(&buff[..(len - 1)])\n                            );\n                            return PowerInfo::Unknown;\n                        }\n                    };\n\n                    let len = c::read(\n                        self.bt_status_fd,\n                        buff.as_mut_ptr() as *mut c::c_void,\n                        buff.len(),\n                    );\n\n                    if len > 0 {\n                        let len = len as usize;\n                        return match str::from_utf8_unchecked(&buff[..(len - 1)]) {\n                            \"Charging\" => PowerInfo::Charging(cap),\n                            \"Discharging\" => PowerInfo::Discharging(cap),\n                            \"Full\" | \"Not charging\" => PowerInfo::Charged,\n                            s => {\n                                error!(\"Unknown battery status value: {}\", s);\n                                PowerInfo::Unknown\n                            }\n                        };\n                    }\n                }\n            }\n            PowerInfo::Unknown\n        } else if self.fd > -1 {\n            PowerInfo::Wired\n        } else {\n            PowerInfo::Unknown\n        }\n    }\n\n    pub fn is_ff_supported(&self) -> bool {\n        self.ff_supported\n    }\n\n    pub fn name(&self) -> &str {\n        &self.name\n    }\n\n    pub fn uuid(&self) -> Uuid {\n        self.uuid\n    }\n\n    pub fn vendor_id(&self) -> Option<u16> {\n        Some(self.vendor_id)\n    }\n\n    pub fn product_id(&self) -> Option<u16> {\n        Some(self.product_id)\n    }\n\n    pub fn ff_device(&self) -> Option<FfDevice> {\n        if self.is_ff_supported() {\n            FfDevice::new(&self.devpath).ok()\n        } else {\n            None\n        }\n    }\n\n    pub fn buttons(&self) -> &[EvCode] {\n        &self.buttons\n    }\n\n    pub fn axes(&self) -> &[EvCode] {\n        &self.axes\n    }\n\n    pub(crate) fn axis_info(&self, nec: EvCode) -> Option<&AxisInfo> {\n        if nec.kind != EV_ABS {\n            None\n        } else {\n            self.axes_info.info.get(nec.code as usize)\n        }\n    }\n}\n\nimpl Drop for Gamepad {\n    fn drop(&mut self) {\n        unsafe {\n            if self.fd >= 0 {\n                c::close(self.fd);\n            }\n            if self.bt_capacity_fd >= 0 {\n                c::close(self.bt_capacity_fd);\n            }\n            if self.bt_status_fd >= 0 {\n                c::close(self.bt_status_fd);\n            }\n        }\n    }\n}\n\nimpl PartialEq for Gamepad {\n    fn eq(&self, other: &Self) -> bool {\n        self.uuid == other.uuid\n    }\n}\n\nfn create_uuid(iid: ioctl::input_id) -> Uuid {\n    let bus = (u32::from(iid.bustype)).to_be();\n    let vendor = iid.vendor.to_be();\n    let product = iid.product.to_be();\n    let version = iid.version.to_be();\n    Uuid::from_fields(\n        bus,\n        vendor,\n        0,\n        &[\n            (product >> 8) as u8,\n            product as u8,\n            0,\n            0,\n            (version >> 8) as u8,\n            version as u8,\n            0,\n            0,\n        ],\n    )\n}\n\nunsafe fn cstr_new(bytes: &[u8]) -> &CStr {\n    CStr::from_bytes_with_nul_unchecked(bytes)\n}\n\n#[cfg(feature = \"serde-serialize\")]\nuse serde::{Deserialize, Serialize};\n\n#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]\n#[cfg_attr(feature = \"serde-serialize\", derive(Serialize, Deserialize))]\npub struct EvCode {\n    kind: u16,\n    code: u16,\n}\n\nimpl EvCode {\n    fn new(kind: u16, code: u16) -> Self {\n        EvCode { kind, code }\n    }\n\n    pub fn into_u32(self) -> u32 {\n        (u32::from(self.kind) << 16) | u32::from(self.code)\n    }\n}\n\nimpl From<input_event> for crate::EvCode {\n    fn from(f: input_event) -> Self {\n        crate::EvCode(EvCode {\n            kind: f.type_,\n            code: f.code,\n        })\n    }\n}\n\nimpl Display for EvCode {\n    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {\n        match self.kind {\n            EV_SYN => f.write_str(\"SYN\")?,\n            EV_KEY => f.write_str(\"KEY\")?,\n            EV_REL => f.write_str(\"REL\")?,\n            EV_ABS => f.write_str(\"ABS\")?,\n            EV_MSC => f.write_str(\"MSC\")?,\n            EV_SW => f.write_str(\"SW\")?,\n            kind => f.write_fmt(format_args!(\"EV_TYPE_{}\", kind))?,\n        }\n\n        f.write_fmt(format_args!(\"({})\", self.code))\n    }\n}\n\n#[derive(Debug, Copy, Clone)]\n#[allow(clippy::enum_variant_names)]\nenum Error {\n    UdevCtx,\n    UdevEnumerate,\n    Errno(Errno, &'static str),\n}\n\nimpl Display for Error {\n    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {\n        match *self {\n            Error::UdevCtx => f.write_str(\"Failed to create udev context\"),\n            Error::UdevEnumerate => f.write_str(\"Failed to create udev enumerate object\"),\n            Error::Errno(e, ctx) => f.write_fmt(format_args!(\"{} failed: {}\", ctx, e)),\n        }\n    }\n}\n\nimpl error::Error for Error {}\n\nfn errno_to_platform_error(errno: Errno, ctx: &'static str) -> PlatformError {\n    PlatformError::Other(Box::new(Error::Errno(errno, ctx)))\n}\n\nconst KEY_MAX: u16 = 0x2ff;\n#[allow(dead_code)]\nconst EV_MAX: u16 = 0x1f;\nconst EV_SYN: u16 = 0x00;\nconst EV_KEY: u16 = 0x01;\nconst EV_REL: u16 = 0x02;\nconst EV_ABS: u16 = 0x03;\nconst EV_MSC: u16 = 0x04;\nconst EV_SW: u16 = 0x05;\nconst ABS_MAX: u16 = 0x3f;\nconst EV_FF: u16 = 0x15;\n\nconst SYN_REPORT: u16 = 0x00;\nconst SYN_DROPPED: u16 = 0x03;\n\nconst BTN_MISC: u16 = 0x100;\nconst BTN_MOUSE: u16 = 0x110;\nconst BTN_JOYSTICK: u16 = 0x120;\nconst BTN_SOUTH: u16 = 0x130;\nconst BTN_EAST: u16 = 0x131;\n#[allow(dead_code)]\nconst BTN_C: u16 = 0x132;\nconst BTN_NORTH: u16 = 0x133;\nconst BTN_WEST: u16 = 0x134;\n#[allow(dead_code)]\nconst BTN_Z: u16 = 0x135;\nconst BTN_TL: u16 = 0x136;\nconst BTN_TR: u16 = 0x137;\nconst BTN_TL2: u16 = 0x138;\nconst BTN_TR2: u16 = 0x139;\nconst BTN_SELECT: u16 = 0x13a;\nconst BTN_START: u16 = 0x13b;\nconst BTN_MODE: u16 = 0x13c;\nconst BTN_THUMBL: u16 = 0x13d;\nconst BTN_THUMBR: u16 = 0x13e;\n\nconst BTN_DPAD_UP: u16 = 0x220;\nconst BTN_DPAD_DOWN: u16 = 0x221;\nconst BTN_DPAD_LEFT: u16 = 0x222;\nconst BTN_DPAD_RIGHT: u16 = 0x223;\n\nconst ABS_X: u16 = 0x00;\nconst ABS_Y: u16 = 0x01;\nconst ABS_Z: u16 = 0x02;\nconst ABS_RX: u16 = 0x03;\nconst ABS_RY: u16 = 0x04;\nconst ABS_RZ: u16 = 0x05;\nconst ABS_HAT0X: u16 = 0x10;\nconst ABS_HAT0Y: u16 = 0x11;\nconst ABS_HAT1X: u16 = 0x12;\nconst ABS_HAT1Y: u16 = 0x13;\nconst ABS_HAT2X: u16 = 0x14;\nconst ABS_HAT2Y: u16 = 0x15;\n\nconst FF_MAX: u16 = FF_GAIN;\nconst FF_SQUARE: u16 = 0x58;\nconst FF_TRIANGLE: u16 = 0x59;\nconst FF_SINE: u16 = 0x5a;\nconst FF_GAIN: u16 = 0x60;\n\npub mod native_ev_codes {\n    use super::*;\n\n    pub const BTN_SOUTH: EvCode = EvCode {\n        kind: EV_KEY,\n        code: super::BTN_SOUTH,\n    };\n    pub const BTN_EAST: EvCode = EvCode {\n        kind: EV_KEY,\n        code: super::BTN_EAST,\n    };\n    pub const BTN_C: EvCode = EvCode {\n        kind: EV_KEY,\n        code: super::BTN_C,\n    };\n    pub const BTN_NORTH: EvCode = EvCode {\n        kind: EV_KEY,\n        code: super::BTN_NORTH,\n    };\n    pub const BTN_WEST: EvCode = EvCode {\n        kind: EV_KEY,\n        code: super::BTN_WEST,\n    };\n    pub const BTN_Z: EvCode = EvCode {\n        kind: EV_KEY,\n        code: super::BTN_Z,\n    };\n    pub const BTN_LT: EvCode = EvCode {\n        kind: EV_KEY,\n        code: super::BTN_TL,\n    };\n    pub const BTN_RT: EvCode = EvCode {\n        kind: EV_KEY,\n        code: super::BTN_TR,\n    };\n    pub const BTN_LT2: EvCode = EvCode {\n        kind: EV_KEY,\n        code: super::BTN_TL2,\n    };\n    pub const BTN_RT2: EvCode = EvCode {\n        kind: EV_KEY,\n        code: super::BTN_TR2,\n    };\n    pub const BTN_SELECT: EvCode = EvCode {\n        kind: EV_KEY,\n        code: super::BTN_SELECT,\n    };\n    pub const BTN_START: EvCode = EvCode {\n        kind: EV_KEY,\n        code: super::BTN_START,\n    };\n    pub const BTN_MODE: EvCode = EvCode {\n        kind: EV_KEY,\n        code: super::BTN_MODE,\n    };\n    pub const BTN_LTHUMB: EvCode = EvCode {\n        kind: EV_KEY,\n        code: super::BTN_THUMBL,\n    };\n    pub const BTN_RTHUMB: EvCode = EvCode {\n        kind: EV_KEY,\n        code: super::BTN_THUMBR,\n    };\n    pub const BTN_DPAD_UP: EvCode = EvCode {\n        kind: EV_KEY,\n        code: super::BTN_DPAD_UP,\n    };\n    pub const BTN_DPAD_DOWN: EvCode = EvCode {\n        kind: EV_KEY,\n        code: super::BTN_DPAD_DOWN,\n    };\n    pub const BTN_DPAD_LEFT: EvCode = EvCode {\n        kind: EV_KEY,\n        code: super::BTN_DPAD_LEFT,\n    };\n    pub const BTN_DPAD_RIGHT: EvCode = EvCode {\n        kind: EV_KEY,\n        code: super::BTN_DPAD_RIGHT,\n    };\n\n    pub const AXIS_LSTICKX: EvCode = EvCode {\n        kind: EV_ABS,\n        code: super::ABS_X,\n    };\n    pub const AXIS_LSTICKY: EvCode = EvCode {\n        kind: EV_ABS,\n        code: super::ABS_Y,\n    };\n    pub const AXIS_LEFTZ: EvCode = EvCode {\n        kind: EV_ABS,\n        code: super::ABS_Z,\n    };\n    pub const AXIS_RSTICKX: EvCode = EvCode {\n        kind: EV_ABS,\n        code: super::ABS_RX,\n    };\n    pub const AXIS_RSTICKY: EvCode = EvCode {\n        kind: EV_ABS,\n        code: super::ABS_RY,\n    };\n    pub const AXIS_RIGHTZ: EvCode = EvCode {\n        kind: EV_ABS,\n        code: super::ABS_RZ,\n    };\n    pub const AXIS_DPADX: EvCode = EvCode {\n        kind: EV_ABS,\n        code: super::ABS_HAT0X,\n    };\n    pub const AXIS_DPADY: EvCode = EvCode {\n        kind: EV_ABS,\n        code: super::ABS_HAT0Y,\n    };\n    pub const AXIS_RT: EvCode = EvCode {\n        kind: EV_ABS,\n        code: super::ABS_HAT1X,\n    };\n    pub const AXIS_LT: EvCode = EvCode {\n        kind: EV_ABS,\n        code: super::ABS_HAT1Y,\n    };\n    pub const AXIS_RT2: EvCode = EvCode {\n        kind: EV_ABS,\n        code: super::ABS_HAT2X,\n    };\n    pub const AXIS_LT2: EvCode = EvCode {\n        kind: EV_ABS,\n        code: super::ABS_HAT2Y,\n    };\n}\n\n#[cfg(test)]\nmod tests {\n    use super::super::ioctl;\n    use super::create_uuid;\n    use uuid::Uuid;\n\n    #[test]\n    fn sdl_uuid() {\n        let x = Uuid::parse_str(\"030000005e0400008e02000020200000\").unwrap();\n        let y = create_uuid(ioctl::input_id {\n            bustype: 0x3,\n            vendor: 0x045e,\n            product: 0x028e,\n            version: 0x2020,\n        });\n        assert_eq!(x, y);\n    }\n}\n"
  },
  {
    "path": "gilrs-core/src/platform/linux/ioctl.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\n\n// Some ioctls are exported by ioctl crate only for x86_64, so we have to define them anyway.\n// Diffing linux/input.h across different architectures (i686, x86_64 and arm) didn't show any\n// difference, so it looks like conditional compilation is not needed.\n#![allow(dead_code)]\n\nuse nix::{ioctl_read, ioctl_read_buf, ioctl_write_int, ioctl_write_ptr, request_code_read};\nuse std::mem::MaybeUninit;\n\n#[cfg(target_env = \"musl\")]\npub type IoctlRequest = libc::c_int;\n#[cfg(not(target_env = \"musl\"))]\npub type IoctlRequest = libc::c_ulong;\n\nioctl_read!(eviocgid, b'E', 0x02, /*struct*/ input_id);\nioctl_write_int!(eviocrmff, b'E', 0x81);\nioctl_write_ptr!(eviocsff, b'E', 0x80, ff_effect);\nioctl_read_buf!(eviocgname, b'E', 0x06, MaybeUninit<u8>);\nioctl_read_buf!(eviocgkey, b'E', 0x18, u8);\n\npub unsafe fn eviocgbit(fd: libc::c_int, ev: u32, len: libc::c_int, buf: *mut u8) -> libc::c_int {\n    ::nix::libc::ioctl(\n        fd,\n        request_code_read!(b'E', 0x20 + ev, len) as IoctlRequest,\n        buf,\n    )\n}\n\npub unsafe fn eviocgabs(fd: ::libc::c_int, abs: u32, buf: *mut input_absinfo) -> libc::c_int {\n    ::nix::libc::ioctl(\n        fd,\n        request_code_read!(b'E', 0x40 + abs, ::std::mem::size_of::<input_absinfo>())\n            as IoctlRequest,\n        buf,\n    )\n}\n\n#[derive(Copy, Clone)]\n#[repr(C)]\npub struct input_event {\n    pub time: libc::timeval,\n    pub type_: u16,\n    pub code: u16,\n    pub value: i32,\n}\n\nimpl ::std::default::Default for input_event {\n    fn default() -> Self {\n        unsafe { ::std::mem::zeroed() }\n    }\n}\n\nimpl ::std::fmt::Debug for input_event {\n    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {\n        write!(\n            f,\n            \"input_event {{ time: {{ tv_sec: {}, tv_usec: {} }}, type_: {}, code: {}, value: {}\",\n            self.time.tv_sec, self.time.tv_usec, self.type_, self.code, self.value\n        )\n    }\n}\n\n#[derive(Copy, Clone)]\n#[repr(C)]\npub struct input_id {\n    pub bustype: u16,\n    pub vendor: u16,\n    pub product: u16,\n    pub version: u16,\n}\n\n#[derive(Copy, Clone, Default, PartialEq, Eq, Debug)]\n#[repr(C)]\npub struct input_absinfo {\n    pub value: i32,\n    pub minimum: i32,\n    pub maximum: i32,\n    pub fuzz: i32,\n    pub flat: i32,\n    pub resolution: i32,\n}\n\n#[derive(Copy, Clone, Default)]\n#[repr(C)]\npub struct ff_replay {\n    pub length: u16,\n    pub delay: u16,\n}\n\n#[derive(Copy, Clone, Default)]\n#[repr(C)]\npub struct ff_trigger {\n    pub button: u16,\n    pub interval: u16,\n}\n\n#[derive(Copy, Clone)]\n#[repr(C)]\npub struct ff_envelope {\n    pub attack_length: u16,\n    pub attack_level: u16,\n    pub fade_length: u16,\n    pub fade_level: u16,\n}\n\n#[derive(Copy, Clone)]\n#[repr(C)]\npub struct ff_constant_effect {\n    pub level: i16,\n    pub envelope: ff_envelope,\n}\n\n#[derive(Copy, Clone)]\n#[repr(C)]\npub struct ff_ramp_effect {\n    pub start_level: i16,\n    pub end_level: i16,\n    pub envelope: ff_envelope,\n}\n\n#[derive(Copy, Clone)]\n#[repr(C)]\npub struct ff_condition_effect {\n    pub right_saturation: u16,\n    pub left_saturation: u16,\n\n    pub right_coeff: i16,\n    pub left_coeff: i16,\n\n    pub deadband: u16,\n    pub center: i16,\n}\n\n#[derive(Copy, Clone)]\n#[repr(C)]\npub struct ff_periodic_effect {\n    pub waveform: u16,\n    pub period: u16,\n    pub magnitude: i16,\n    pub offset: i16,\n    pub phase: u16,\n\n    pub envelope: ff_envelope,\n\n    pub custom_len: u32,\n    pub custom_data: *mut i16,\n}\n\n#[derive(Copy, Clone)]\n#[repr(C)]\npub struct ff_rumble_effect {\n    pub strong_magnitude: u16,\n    pub weak_magnitude: u16,\n}\n\n#[derive(Copy, Clone)]\n#[repr(C)]\npub struct ff_effect {\n    pub type_: u16,\n    pub id: i16,\n    pub direction: u16,\n    pub trigger: ff_trigger,\n    pub replay: ff_replay,\n    // FIXME this is actually a union\n    #[cfg(target_pointer_width = \"64\")]\n    pub u: [u64; 4],\n    #[cfg(target_pointer_width = \"32\")]\n    pub u: [u32; 7],\n}\n"
  },
  {
    "path": "gilrs-core/src/platform/linux/mod.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\n// Copyright 2016 GilRs Developers\nmod ff;\nmod gamepad;\nmod ioctl;\nmod udev;\n\npub use self::ff::Device as FfDevice;\npub use self::gamepad::{native_ev_codes, EvCode, Gamepad, Gilrs};\n\npub const IS_Y_AXIS_REVERSED: bool = true;\n"
  },
  {
    "path": "gilrs-core/src/platform/linux/udev.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\n\nuse libc as c;\nuse libudev_sys as ud;\nuse std::ffi::{CStr, CString};\nuse std::os::raw::c_char;\nuse std::ptr;\n\n#[derive(Debug)]\npub struct Udev(*mut ud::udev);\n\nimpl Udev {\n    pub fn new() -> Option<Self> {\n        let u = unsafe { ud::udev_new() };\n        if u.is_null() {\n            None\n        } else {\n            Some(Udev(u))\n        }\n    }\n\n    pub fn enumerate(&self) -> Option<Enumerate> {\n        let en = unsafe { ud::udev_enumerate_new(self.0) };\n        if en.is_null() {\n            None\n        } else {\n            let en = Enumerate(en);\n            Some(en)\n        }\n    }\n}\n\nimpl Drop for Udev {\n    fn drop(&mut self) {\n        unsafe {\n            ud::udev_unref(self.0);\n        }\n    }\n}\n\nimpl Clone for Udev {\n    fn clone(&self) -> Self {\n        Udev(unsafe { ud::udev_ref(self.0) })\n    }\n}\n\npub struct Enumerate(*mut ud::udev_enumerate);\n\nimpl Enumerate {\n    pub fn scan_devices(&self) {\n        // TODO: Check for error\n        let _ = unsafe { ud::udev_enumerate_scan_devices(self.0) };\n    }\n\n    pub fn add_match_property(&self, key: &CStr, val: &CStr) {\n        // TODO: Check for error\n        unsafe {\n            ud::udev_enumerate_add_match_property(self.0, key.as_ptr(), val.as_ptr());\n        }\n    }\n\n    pub fn add_match_subsystem(&self, subsystem: &CStr) {\n        // TODO: Check for error\n        unsafe {\n            ud::udev_enumerate_add_match_subsystem(self.0, subsystem.as_ptr());\n        }\n    }\n\n    pub fn iter(&self) -> DeviceIterator {\n        DeviceIterator(unsafe { ud::udev_enumerate_get_list_entry(self.0) })\n    }\n}\n\nimpl Drop for Enumerate {\n    fn drop(&mut self) {\n        unsafe {\n            ud::udev_enumerate_unref(self.0);\n        }\n    }\n}\n\npub struct DeviceIterator(*mut ud::udev_list_entry);\n\nimpl Iterator for DeviceIterator {\n    type Item = CString;\n\n    fn next(&mut self) -> Option<CString> {\n        if self.0.is_null() {\n            None\n        } else {\n            let p_name = unsafe { ud::udev_list_entry_get_name(self.0) };\n            let name = if p_name.is_null() {\n                return None;\n            } else {\n                unsafe { CStr::from_ptr(p_name).to_owned() }\n            };\n            self.0 = unsafe { ud::udev_list_entry_get_next(self.0) };\n            Some(name)\n        }\n    }\n}\n\npub struct Device(*mut ud::udev_device);\n\nimpl Device {\n    pub fn from_syspath(udev: &Udev, path: &CStr) -> Option<Self> {\n        let dev = unsafe { ud::udev_device_new_from_syspath(udev.0, path.as_ptr()) };\n        if dev.is_null() {\n            None\n        } else {\n            Some(Device(dev))\n        }\n    }\n\n    pub fn syspath(&self) -> &CStr {\n        // Always returns cstring\n        unsafe { CStr::from_ptr(ud::udev_device_get_syspath(self.0)) }\n    }\n\n    pub fn devnode(&self) -> Option<&CStr> {\n        unsafe {\n            let s = ud::udev_device_get_devnode(self.0);\n            if s.is_null() {\n                None\n            } else {\n                Some(CStr::from_ptr(s))\n            }\n        }\n    }\n\n    #[allow(dead_code)]\n    pub fn properties(&self) -> PropertyIterator {\n        let prop = unsafe { ud::udev_device_get_properties_list_entry(self.0) };\n        PropertyIterator(prop)\n    }\n\n    pub fn action(&self) -> Option<&CStr> {\n        unsafe {\n            let s = ud::udev_device_get_action(self.0);\n            if s.is_null() {\n                None\n            } else {\n                Some(CStr::from_ptr(s))\n            }\n        }\n    }\n\n    pub fn property_value(&self, key: &CStr) -> Option<&CStr> {\n        unsafe {\n            let s = ud::udev_device_get_property_value(self.0, key.as_ptr());\n            if s.is_null() {\n                None\n            } else {\n                Some(CStr::from_ptr(s))\n            }\n        }\n    }\n}\n\nimpl Clone for Device {\n    fn clone(&self) -> Self {\n        unsafe { Device(ud::udev_device_ref(self.0)) }\n    }\n}\n\nimpl Drop for Device {\n    fn drop(&mut self) {\n        unsafe {\n            ud::udev_device_unref(self.0);\n        }\n    }\n}\n\n#[allow(dead_code)]\npub struct PropertyIterator(*mut ud::udev_list_entry);\n\nimpl Iterator for PropertyIterator {\n    type Item = (String, String);\n\n    fn next(&mut self) -> Option<(String, String)> {\n        if self.0.is_null() {\n            None\n        } else {\n            let p_name = unsafe { ud::udev_list_entry_get_name(self.0) };\n            let p_val = unsafe { ud::udev_list_entry_get_value(self.0) };\n\n            let name = if p_name.is_null() {\n                return None;\n            } else {\n                unsafe { CStr::from_ptr(p_name).to_string_lossy().into_owned() }\n            };\n\n            let value = if p_val.is_null() {\n                return None;\n            } else {\n                unsafe { CStr::from_ptr(p_val).to_string_lossy().into_owned() }\n            };\n\n            self.0 = unsafe { ud::udev_list_entry_get_next(self.0) };\n            Some((name, value))\n        }\n    }\n}\n\n#[derive(Debug)]\npub struct Monitor(*mut ud::udev_monitor);\n\nimpl Monitor {\n    pub fn new(udev: &Udev) -> Option<Self> {\n        unsafe {\n            let monitor =\n                ud::udev_monitor_new_from_netlink(udev.0, c\"udev\".as_ptr() as *const c_char);\n            if monitor.is_null() {\n                None\n            } else {\n                ud::udev_monitor_filter_add_match_subsystem_devtype(\n                    monitor,\n                    c\"input\".as_ptr() as *const c_char,\n                    ptr::null(),\n                );\n                ud::udev_monitor_enable_receiving(monitor);\n                Some(Monitor(monitor))\n            }\n        }\n    }\n\n    pub fn wait_hotplug_available(&self) -> bool {\n        unsafe {\n            let mut fds = c::pollfd {\n                fd: ud::udev_monitor_get_fd(self.0),\n                events: c::POLLIN,\n                revents: 0,\n            };\n            (c::poll(&mut fds, 1, -1) == 1) && (fds.revents & c::POLLIN != 0)\n        }\n    }\n\n    pub fn device(&self) -> Device {\n        Device(unsafe { ud::udev_monitor_receive_device(self.0) })\n    }\n}\n\nimpl Drop for Monitor {\n    fn drop(&mut self) {\n        unsafe {\n            ud::udev_monitor_unref(self.0);\n        }\n    }\n}\n"
  },
  {
    "path": "gilrs-core/src/platform/macos/ff.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\n\nuse std::time::Duration;\n\n#[derive(Debug)]\n/// Represents gamepad. Reexported as FfDevice\npub struct Device;\n\nimpl Device {\n    /// Sets magnitude for strong and weak ff motors.\n    pub fn set_ff_state(&mut self, _strong: u16, _weak: u16, _min_duration: Duration) {}\n}\n"
  },
  {
    "path": "gilrs-core/src/platform/macos/gamepad.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\n\nuse super::io_kit::*;\nuse super::FfDevice;\nuse crate::{AxisInfo, Event, EventType, PlatformError, PowerInfo};\n\nuse objc2_core_foundation::{kCFRunLoopDefaultMode, CFRetained, CFRunLoop, Type};\nuse objc2_io_kit::{\n    kHIDPage_GenericDesktop, kHIDPage_VendorDefinedStart, kHIDUsage_GD_GamePad,\n    kHIDUsage_GD_Joystick, kHIDUsage_GD_MultiAxisController, IOHIDDevice, IOHIDElement, IOHIDValue,\n    IOReturn,\n};\nuse uuid::Uuid;\nuse vec_map::VecMap;\n\nuse std::fmt::{Display, Formatter, Result as FmtResult};\nuse std::os::raw::c_void;\nuse std::ptr::NonNull;\nuse std::sync::mpsc::{self, Receiver, Sender};\nuse std::sync::{Arc, Mutex};\nuse std::thread;\nuse std::time::Duration;\n\n#[derive(Debug)]\npub struct Gilrs {\n    gamepads: Vec<Gamepad>,\n    device_infos: Arc<Mutex<Vec<DeviceInfo>>>,\n    rx: Receiver<(Event, Option<Device>)>,\n}\n\nimpl Gilrs {\n    pub(crate) fn new() -> Result<Self, PlatformError> {\n        let gamepads = Vec::new();\n        let device_infos = Arc::new(Mutex::new(Vec::new()));\n\n        let (tx, rx) = mpsc::channel();\n        Self::spawn_thread(tx, device_infos.clone());\n\n        Ok(Gilrs {\n            gamepads,\n            device_infos,\n            rx,\n        })\n    }\n\n    fn spawn_thread(\n        tx: Sender<(Event, Option<Device>)>,\n        device_infos: Arc<Mutex<Vec<DeviceInfo>>>,\n    ) {\n        thread::Builder::new()\n            .name(\"gilrs\".to_owned())\n            .spawn(move || {\n                let manager = match new_manager() {\n                    Some(manager) => manager,\n                    None => {\n                        error!(\"Failed to create IOHIDManager object\");\n                        return;\n                    }\n                };\n\n                let rl = CFRunLoop::current().unwrap();\n\n                // SAFETY: We pass the current thread's runloop, so the\n                // callback will be run on this thread below.\n                unsafe { manager.schedule_with_run_loop(&rl, kCFRunLoopDefaultMode.unwrap()) };\n\n                // SAFETY: The contexts pointer is a valid pointer.\n                let context = &(tx.clone(), device_infos.clone()) as *const Context as *mut c_void;\n                unsafe {\n                    manager.register_device_matching_callback(Some(device_matching_cb), context)\n                };\n\n                // SAFETY: Same as above.\n                let context = &(tx.clone(), device_infos.clone()) as *const Context as *mut c_void;\n                unsafe {\n                    manager.register_device_removal_callback(Some(device_removal_cb), context)\n                };\n\n                // SAFETY: Same as above.\n                let context = &(tx, device_infos) as *const Context as *mut c_void;\n                unsafe { manager.register_input_value_callback(Some(input_value_cb), context) };\n\n                CFRunLoop::run();\n\n                // SAFETY: There are no threading requirements from this.\n                unsafe { manager.unschedule_from_run_loop(&rl, kCFRunLoopDefaultMode.unwrap()) };\n            })\n            .expect(\"failed to spawn thread\");\n    }\n\n    pub(crate) fn next_event(&mut self) -> Option<Event> {\n        let event = self.rx.try_recv().ok();\n        self.handle_event(event)\n    }\n\n    pub(crate) fn next_event_blocking(&mut self, timeout: Option<Duration>) -> Option<Event> {\n        let event = if let Some(timeout) = timeout {\n            self.rx.recv_timeout(timeout).ok()\n        } else {\n            self.rx.recv().ok()\n        };\n\n        self.handle_event(event)\n    }\n\n    fn handle_event(&mut self, event: Option<(Event, Option<Device>)>) -> Option<Event> {\n        match event {\n            Some((event, Some(device))) => {\n                if event.event == EventType::Connected {\n                    if self.gamepads.get(event.id).is_some() {\n                        self.gamepads[event.id].is_connected = true;\n                    } else {\n                        match Gamepad::open(&device.0) {\n                            Some(gamepad) => {\n                                self.gamepads.push(gamepad);\n                            }\n                            None => {\n                                error!(\"Failed to open gamepad: {:?}\", event.id);\n                                return None;\n                            }\n                        };\n                    }\n                }\n                Some(event)\n            }\n            Some((event, None)) => {\n                if event.event == EventType::Disconnected {\n                    match self.gamepads.get_mut(event.id) {\n                        Some(gamepad) => {\n                            match self.device_infos.lock().unwrap().get_mut(event.id) {\n                                Some(device_info) => device_info.is_connected = false,\n                                None => {\n                                    error!(\"Failed to find device_info: {:?}\", event.id);\n                                    return None;\n                                }\n                            };\n                            gamepad.is_connected = false;\n                        }\n                        None => {\n                            error!(\"Failed to find gamepad: {:?}\", event.id);\n                            return None;\n                        }\n                    }\n                }\n                Some(event)\n            }\n            None => None,\n        }\n    }\n\n    pub fn gamepad(&self, id: usize) -> Option<&Gamepad> {\n        self.gamepads.get(id)\n    }\n\n    /// Returns index greater than index of last connected gamepad.\n    pub fn last_gamepad_hint(&self) -> usize {\n        self.gamepads.len()\n    }\n}\n\n#[derive(Debug)]\n#[allow(dead_code)]\npub struct Gamepad {\n    name: String,\n    vendor: Option<u16>,\n    product: Option<u16>,\n    uuid: Uuid,\n    entry_id: u64,\n    location_id: u32,\n    page: u32,\n    usage: u32,\n    axes_info: VecMap<AxisInfo>,\n    axes: Vec<EvCode>,\n    hats: Vec<EvCode>,\n    buttons: Vec<EvCode>,\n    is_connected: bool,\n}\n\nimpl Gamepad {\n    fn open(device: &IOHIDDevice) -> Option<Gamepad> {\n        let io_service = match IOService::new(device.service()) {\n            Some(io_service) => io_service,\n            None => {\n                error!(\"Failed to get device service\");\n                return None;\n            }\n        };\n\n        let entry_id = match io_service.get_registry_entry_id() {\n            Some(entry_id) => entry_id,\n            None => {\n                error!(\"Failed to get entry id of device\");\n                return None;\n            }\n        };\n\n        let location_id = match device.get_location_id() {\n            Some(location_id) => location_id,\n            None => {\n                error!(\"Failed to get location id of device\");\n                return None;\n            }\n        };\n\n        let page = match device.get_page() {\n            Some(page) => {\n                if page >= kHIDPage_VendorDefinedStart {\n                    error!(\"Device HID page is Vendor Defined. {device:?}\");\n                    return None;\n                }\n\n                if page == kHIDPage_GenericDesktop {\n                    page\n                } else {\n                    error!(\"Failed to get valid device. Expecting kHIDPage_GenericDesktop. Got 0x{:X?}\", page);\n                    return None;\n                }\n            }\n            None => {\n                error!(\"Failed to get page of device\");\n                return None;\n            }\n        };\n\n        let usage = match device.get_usage() {\n            Some(usage) => {\n                if usage == kHIDUsage_GD_GamePad\n                    || usage == kHIDUsage_GD_Joystick\n                    || usage == kHIDUsage_GD_MultiAxisController\n                {\n                    usage\n                } else {\n                    error!(\"Failed to get valid device: {:?}\", usage);\n                    return None;\n                }\n            }\n            None => {\n                error!(\"Failed to get usage of device\");\n                return None;\n            }\n        };\n\n        let name = device.get_name().unwrap_or_else(|| {\n            warn!(\"Failed to get name of device\");\n            \"Unknown\".into()\n        });\n\n        let uuid = Self::create_uuid(&device).unwrap_or_default();\n\n        let mut gamepad = Gamepad {\n            name,\n            vendor: device.get_vendor_id(),\n            product: device.get_product_id(),\n            uuid,\n            entry_id,\n            location_id,\n            page,\n            usage,\n            axes_info: VecMap::with_capacity(8),\n            axes: Vec::with_capacity(8),\n            hats: Vec::with_capacity(4),\n            buttons: Vec::with_capacity(16),\n            is_connected: true,\n        };\n        gamepad.collect_axes_and_buttons(&device_elements(&device));\n\n        Some(gamepad)\n    }\n\n    fn create_uuid(device: &IOHIDDevice) -> Option<Uuid> {\n        // SDL always uses USB bus for UUID\n        let bustype = u32::to_be(0x03);\n\n        let vendor_id = match device.get_vendor_id() {\n            Some(vendor_id) => vendor_id.to_be(),\n            None => {\n                warn!(\"Failed to get vendor id of device\");\n                0\n            }\n        };\n\n        let product_id = match device.get_product_id() {\n            Some(product_id) => product_id.to_be(),\n            None => {\n                warn!(\"Failed to get product id of device\");\n                0\n            }\n        };\n\n        let version = match device.get_version() {\n            Some(version) => version.to_be(),\n            None => {\n                warn!(\"Failed to get version of device\");\n                0\n            }\n        };\n\n        if vendor_id == 0 && product_id == 0 && version == 0 {\n            None\n        } else {\n            Some(Uuid::from_fields(\n                bustype,\n                vendor_id,\n                0,\n                &[\n                    (product_id >> 8) as u8,\n                    product_id as u8,\n                    0,\n                    0,\n                    (version >> 8) as u8,\n                    version as u8,\n                    0,\n                    0,\n                ],\n            ))\n        }\n    }\n\n    pub fn name(&self) -> &str {\n        &self.name\n    }\n\n    pub fn vendor_id(&self) -> Option<u16> {\n        self.vendor\n    }\n\n    pub fn product_id(&self) -> Option<u16> {\n        self.product\n    }\n\n    pub fn uuid(&self) -> Uuid {\n        self.uuid\n    }\n\n    pub fn power_info(&self) -> PowerInfo {\n        PowerInfo::Unknown\n    }\n\n    pub fn is_ff_supported(&self) -> bool {\n        false\n    }\n\n    /// Creates Ffdevice corresponding to this gamepad.\n    pub fn ff_device(&self) -> Option<FfDevice> {\n        Some(FfDevice)\n    }\n\n    pub fn buttons(&self) -> &[EvCode] {\n        &self.buttons\n    }\n\n    pub fn axes(&self) -> &[EvCode] {\n        &self.axes\n    }\n\n    pub(crate) fn axis_info(&self, nec: EvCode) -> Option<&AxisInfo> {\n        self.axes_info.get(nec.usage as usize)\n    }\n\n    pub fn is_connected(&self) -> bool {\n        self.is_connected\n    }\n\n    fn collect_axes_and_buttons(&mut self, elements: &Vec<CFRetained<IOHIDElement>>) {\n        let mut cookies = Vec::new();\n\n        self.collect_axes(elements, &mut cookies);\n        self.axes.sort_by_key(|axis| axis.usage);\n        self.hats.sort_by_key(|axis| axis.usage);\n        // Because \"hat is axis\" is a gilrs thing, we want to ensure that all hats are at the end of\n        // the axis vector, so the SDL mappings still work.\n        self.axes.extend(&self.hats);\n\n        self.collect_buttons(elements, &mut cookies);\n        self.buttons.sort_by_key(|button| button.usage);\n    }\n\n    fn collect_axes(&mut self, elements: &Vec<CFRetained<IOHIDElement>>, cookies: &mut Vec<u32>) {\n        for element in elements {\n            let type_ = element.r#type();\n            let cookie = element.cookie();\n            let page = element.usage_page();\n            let usage = element.usage();\n\n            if element_is_collection(type_) {\n                let children = element_children(element);\n                self.collect_axes(&children, cookies);\n            } else if element_is_axis(type_, page, usage) && !cookies.contains(&cookie) {\n                cookies.push(cookie);\n                self.axes_info.insert(\n                    usage as usize,\n                    AxisInfo {\n                        min: element.logical_min() as _,\n                        max: element.logical_max() as _,\n                        deadzone: None,\n                    },\n                );\n                self.axes.push(EvCode::new(page, usage));\n            } else if element_is_hat(type_, page, usage) && !cookies.contains(&cookie) {\n                cookies.push(cookie);\n                self.axes_info.insert(\n                    usage as usize,\n                    AxisInfo {\n                        min: -1,\n                        max: 1,\n                        deadzone: None,\n                    },\n                );\n                self.hats.push(EvCode::new(page, usage));\n                // All hat switches are translated into *two* axes\n                self.axes_info.insert(\n                    (usage + 1) as usize, // \"+ 1\" is assumed for usage of 2nd hat switch axis\n                    AxisInfo {\n                        min: -1,\n                        max: 1,\n                        deadzone: None,\n                    },\n                );\n                self.hats.push(EvCode::new(page, usage + 1));\n            }\n        }\n    }\n\n    fn collect_buttons(\n        &mut self,\n        elements: &Vec<CFRetained<IOHIDElement>>,\n        cookies: &mut Vec<u32>,\n    ) {\n        for element in elements {\n            let type_ = element.r#type();\n            let cookie = element.cookie();\n            let page = element.usage_page();\n            let usage = element.usage();\n\n            if element_is_collection(type_) {\n                let children = element_children(element);\n                self.collect_buttons(&children, cookies);\n            } else if element_is_button(type_, page, usage) && !cookies.contains(&cookie) {\n                cookies.push(cookie);\n                self.buttons.push(EvCode::new(page, usage));\n            }\n        }\n    }\n}\n\n#[derive(Debug)]\nstruct DeviceInfo {\n    entry_id: u64,\n    location_id: u32,\n    is_connected: bool,\n}\n#[cfg(feature = \"serde-serialize\")]\nuse serde::{Deserialize, Serialize};\n\n#[cfg_attr(feature = \"serde-serialize\", derive(Serialize, Deserialize))]\n#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]\npub struct EvCode {\n    page: u32,\n    usage: u32,\n}\n\nimpl EvCode {\n    fn new(page: u32, usage: u32) -> Self {\n        EvCode { page, usage }\n    }\n\n    pub fn into_u32(self) -> u32 {\n        (self.page << 16) | self.usage\n    }\n}\n\nimpl From<IOHIDElement> for crate::EvCode {\n    fn from(e: IOHIDElement) -> Self {\n        crate::EvCode(EvCode {\n            page: e.usage_page(),\n            usage: e.usage(),\n        })\n    }\n}\n\nimpl Display for EvCode {\n    fn fmt(&self, f: &mut Formatter) -> FmtResult {\n        match self.page {\n            PAGE_GENERIC_DESKTOP => f.write_str(\"GENERIC_DESKTOP\")?,\n            PAGE_BUTTON => f.write_str(\"BUTTON\")?,\n            page => f.write_fmt(format_args!(\"PAGE_{}\", page))?,\n        }\n        f.write_fmt(format_args!(\"({})\", self.usage))\n    }\n}\n\npub mod native_ev_codes {\n    use super::*;\n\n    pub const AXIS_LSTICKX: EvCode = EvCode {\n        page: super::PAGE_GENERIC_DESKTOP,\n        usage: super::USAGE_AXIS_LSTICKX,\n    };\n    pub const AXIS_LSTICKY: EvCode = EvCode {\n        page: super::PAGE_GENERIC_DESKTOP,\n        usage: super::USAGE_AXIS_LSTICKY,\n    };\n    pub const AXIS_LEFTZ: EvCode = EvCode {\n        page: super::PAGE_GENERIC_DESKTOP,\n        usage: super::USAGE_AXIS_LEFTZ,\n    };\n    pub const AXIS_RSTICKX: EvCode = EvCode {\n        page: super::PAGE_GENERIC_DESKTOP,\n        usage: super::USAGE_AXIS_RSTICKX,\n    };\n    pub const AXIS_RSTICKY: EvCode = EvCode {\n        page: super::PAGE_GENERIC_DESKTOP,\n        usage: super::USAGE_AXIS_RSTICKY,\n    };\n    pub const AXIS_RIGHTZ: EvCode = EvCode {\n        page: super::PAGE_GENERIC_DESKTOP,\n        usage: super::USAGE_AXIS_RIGHTZ,\n    };\n    pub const AXIS_DPADX: EvCode = EvCode {\n        page: super::PAGE_GENERIC_DESKTOP,\n        usage: super::USAGE_AXIS_DPADX,\n    };\n    pub const AXIS_DPADY: EvCode = EvCode {\n        page: super::PAGE_GENERIC_DESKTOP,\n        usage: super::USAGE_AXIS_DPADY,\n    };\n    pub const AXIS_RT: EvCode = EvCode {\n        page: super::PAGE_GENERIC_DESKTOP,\n        usage: super::USAGE_AXIS_RT,\n    };\n    pub const AXIS_LT: EvCode = EvCode {\n        page: super::PAGE_GENERIC_DESKTOP,\n        usage: super::USAGE_AXIS_LT,\n    };\n    pub const AXIS_RT2: EvCode = EvCode {\n        page: super::PAGE_SIMULATION,\n        usage: super::USAGE_AXIS_RT2,\n    };\n    pub const AXIS_LT2: EvCode = EvCode {\n        page: super::PAGE_SIMULATION,\n        usage: super::USAGE_AXIS_LT2,\n    };\n\n    pub const BTN_SOUTH: EvCode = EvCode {\n        page: super::PAGE_BUTTON,\n        usage: super::USAGE_BTN_SOUTH,\n    };\n    pub const BTN_EAST: EvCode = EvCode {\n        page: super::PAGE_BUTTON,\n        usage: super::USAGE_BTN_EAST,\n    };\n    pub const BTN_C: EvCode = EvCode {\n        page: super::PAGE_BUTTON,\n        usage: super::USAGE_BTN_C,\n    };\n    pub const BTN_NORTH: EvCode = EvCode {\n        page: super::PAGE_BUTTON,\n        usage: super::USAGE_BTN_NORTH,\n    };\n    pub const BTN_WEST: EvCode = EvCode {\n        page: super::PAGE_BUTTON,\n        usage: super::USAGE_BTN_WEST,\n    };\n    pub const BTN_Z: EvCode = EvCode {\n        page: super::PAGE_BUTTON,\n        usage: super::USAGE_BTN_Z,\n    };\n    pub const BTN_LT: EvCode = EvCode {\n        page: super::PAGE_BUTTON,\n        usage: super::USAGE_BTN_LT,\n    };\n    pub const BTN_RT: EvCode = EvCode {\n        page: super::PAGE_BUTTON,\n        usage: super::USAGE_BTN_RT,\n    };\n    pub const BTN_LT2: EvCode = EvCode {\n        page: super::PAGE_BUTTON,\n        usage: super::USAGE_BTN_LT2,\n    };\n    pub const BTN_RT2: EvCode = EvCode {\n        page: super::PAGE_BUTTON,\n        usage: super::USAGE_BTN_RT2,\n    };\n    pub const BTN_SELECT: EvCode = EvCode {\n        page: super::PAGE_BUTTON,\n        usage: super::USAGE_BTN_SELECT,\n    };\n    pub const BTN_START: EvCode = EvCode {\n        page: super::PAGE_BUTTON,\n        usage: super::USAGE_BTN_START,\n    };\n    pub const BTN_MODE: EvCode = EvCode {\n        page: super::PAGE_BUTTON,\n        usage: super::USAGE_BTN_MODE,\n    };\n    pub const BTN_LTHUMB: EvCode = EvCode {\n        page: super::PAGE_BUTTON,\n        usage: super::USAGE_BTN_LTHUMB,\n    };\n    pub const BTN_RTHUMB: EvCode = EvCode {\n        page: super::PAGE_BUTTON,\n        usage: super::USAGE_BTN_RTHUMB,\n    };\n\n    pub const BTN_DPAD_UP: EvCode = EvCode {\n        page: super::PAGE_BUTTON,\n        usage: super::USAGE_BTN_DPAD_UP,\n    };\n    pub const BTN_DPAD_DOWN: EvCode = EvCode {\n        page: super::PAGE_BUTTON,\n        usage: super::USAGE_BTN_DPAD_DOWN,\n    };\n    pub const BTN_DPAD_LEFT: EvCode = EvCode {\n        page: super::PAGE_BUTTON,\n        usage: super::USAGE_BTN_DPAD_LEFT,\n    };\n    pub const BTN_DPAD_RIGHT: EvCode = EvCode {\n        page: super::PAGE_BUTTON,\n        usage: super::USAGE_BTN_DPAD_RIGHT,\n    };\n}\n\ntype Context = (Sender<(Event, Option<Device>)>, Arc<Mutex<Vec<DeviceInfo>>>);\n\nextern \"C-unwind\" fn device_matching_cb(\n    context: *mut c_void,\n    _result: IOReturn,\n    _sender: *mut c_void,\n    device: NonNull<IOHIDDevice>,\n) {\n    // SAFETY: Validity of the pointer is upheld by the caller.\n    let device = unsafe { device.as_ref() };\n    // SAFETY: The context is the one we passed in `Gilrs::spawn_thread`.\n    let (tx, device_infos): &Context = unsafe { &*(context as *mut _) };\n\n    let io_service = match IOService::new(device.service()) {\n        Some(io_service) => io_service,\n        None => {\n            error!(\"Failed to get device service\");\n            return;\n        }\n    };\n\n    let entry_id = match io_service.get_registry_entry_id() {\n        Some(entry_id) => entry_id,\n        None => {\n            error!(\"Failed to get entry id of device\");\n            return;\n        }\n    };\n\n    // Filter devices which will not succed in open(). If devices are added to the\n    // DeviceInfo vec, it will cause a mismatch of IDs as they're derived from\n    // the length of the DeviceInfo Vec, but devices which fail to open() will not\n    // be pushed into the Gilrs inner gamepads vec, and panics will ensue.\n    //\n    // Try to open the device early, and if it fails, do not add it to device_infos\n    match Gamepad::open(&device) {\n        Some(gamepad) => drop(gamepad),\n        None => {\n            warn!(\"Failed to open device {device:?}. Skipping.\");\n            return;\n        }\n    }\n\n    let mut device_infos = device_infos.lock().unwrap();\n    let id = match device_infos\n        .iter()\n        .position(|info| info.entry_id == entry_id && info.is_connected)\n    {\n        Some(id) => {\n            info!(\"Device is already registered: {:?}\", entry_id);\n            id\n        }\n        None => {\n            let location_id = match device.get_location_id() {\n                Some(location_id) => location_id,\n                None => {\n                    error!(\"Failed to get location id of device\");\n                    return;\n                }\n            };\n\n            device_infos.push(DeviceInfo {\n                entry_id,\n                location_id,\n                is_connected: true,\n            });\n\n            device_infos.len() - 1\n        }\n    };\n    let _ = tx.send((\n        Event::new(id, EventType::Connected),\n        Some(Device(device.retain())),\n    ));\n}\n\n#[allow(clippy::type_complexity)]\nunsafe extern \"C-unwind\" fn device_removal_cb(\n    context: *mut c_void,\n    _result: IOReturn,\n    _sender: *mut c_void,\n    device: NonNull<IOHIDDevice>,\n) {\n    // SAFETY: Validity of the pointer is upheld by the caller.\n    let device = unsafe { device.as_ref() };\n    // SAFETY: The context is the one we passed in `Gilrs::spawn_thread`.\n    let (tx, device_infos): &Context = unsafe { &*(context as *mut _) };\n\n    let location_id = match device.get_location_id() {\n        Some(location_id) => location_id,\n        None => {\n            error!(\"Failed to get location id of device\");\n            return;\n        }\n    };\n\n    let device_infos = device_infos.lock().unwrap();\n    let id = match device_infos\n        .iter()\n        .position(|info| info.location_id == location_id && info.is_connected)\n    {\n        Some(id) => id,\n        None => {\n            warn!(\"Failed to find device: {:?}\", location_id);\n            return;\n        }\n    };\n\n    let _ = tx.send((Event::new(id, EventType::Disconnected), None));\n}\n\n#[allow(clippy::type_complexity)]\nunsafe extern \"C-unwind\" fn input_value_cb(\n    context: *mut c_void,\n    _result: IOReturn,\n    sender: *mut c_void,\n    value: NonNull<IOHIDValue>,\n) {\n    // SAFETY: Validity of the pointer is upheld by the caller.\n    let value = unsafe { value.as_ref() };\n    // SAFETY: The context is the one we passed in `Gilrs::spawn_thread`.\n    let (tx, device_infos): &Context = unsafe { &*(context as *mut _) };\n\n    // SAFETY: TODO.\n    let device = match unsafe { sender.cast::<IOHIDDevice>().as_ref() } {\n        Some(device) => device,\n        None => {\n            error!(\"Failed to get device\");\n            return;\n        }\n    };\n\n    let io_service = match device.get_service() {\n        Some(io_service) => io_service,\n        None => {\n            error!(\"Failed to get device service\");\n            return;\n        }\n    };\n\n    let entry_id = match io_service.get_registry_entry_id() {\n        Some(entry_id) => entry_id,\n        None => {\n            error!(\"Failed to get entry id of device\");\n            return;\n        }\n    };\n\n    let device_infos = device_infos.lock().unwrap();\n    let id = match device_infos\n        .iter()\n        .position(|info| info.entry_id == entry_id && info.is_connected)\n    {\n        Some(id) => id,\n        None => {\n            warn!(\"Failed to find device: {:?}\", entry_id);\n            return;\n        }\n    };\n\n    let element = value.element();\n\n    let type_ = element.r#type();\n    let page = element.usage_page();\n    let usage = element.usage();\n\n    if element_is_axis(type_, page, usage) {\n        let event = Event::new(\n            id,\n            EventType::AxisValueChanged(\n                value.integer_value() as i32,\n                crate::EvCode(EvCode { page, usage }),\n            ),\n        );\n        let _ = tx.send((event, None));\n    } else if element_is_button(type_, page, usage) {\n        if value.integer_value() == 0 {\n            let event = Event::new(\n                id,\n                EventType::ButtonReleased(crate::EvCode(EvCode { page, usage })),\n            );\n            let _ = tx.send((event, None));\n        } else {\n            let event = Event::new(\n                id,\n                EventType::ButtonPressed(crate::EvCode(EvCode { page, usage })),\n            );\n            let _ = tx.send((event, None));\n        }\n    } else if element_is_hat(type_, page, usage) {\n        // Hat switch values are reported with a range of usually 8 numbers (sometimes 4). The logic\n        // below uses the reported min/max values of that range to map that onto a range of 0-7 for\n        // the directions (and any other value indicates the center position). Lucky for us, they\n        // always start with \"up\" as the lowest number and proceed clockwise. See similar handling\n        // here https://github.com/spurious/SDL-mirror/blob/094b2f68dd7fc9af167f905e10625e103a131459/src/joystick/darwin/SDL_sysjoystick.c#L976-L1028\n        //\n        //          up\n        //       7  0  1\n        //        \\ | /\n        // left 6 - ? - 2 right       (After mapping)\n        //        / | \\\n        //       5  4  3\n        //         down\n        let range = element.logical_max() - element.logical_min() + 1;\n        let shifted_value = value.integer_value() - element.logical_min();\n        let dpad_value = match range {\n            4 => shifted_value * 2, // 4-position hat switch - scale it up to 8\n            8 => shifted_value,     // 8-position hat switch - no adjustment necessary\n            _ => -1, // Neither 4 nor 8 positions, we don't know what to do - default to centered\n        };\n        // At this point, the value should be normalized to the 0-7 directional values (or center\n        // for any other value). The dpad is a hat switch on macOS, but on other platforms dpads are\n        // either buttons or a pair of axes that get converted to button events by the\n        // `axis_dpad_to_button` filter.  We will emulate axes here and let that filter do the\n        // button conversion, because it is safer and easier than making separate logic for button\n        // conversion that may diverge in subtle ways from the axis conversion logic.  The most\n        // practical outcome of this conversion is that there are extra \"released\" axis events for\n        // the unused axis. For example, pressing just \"up\" will also give you a \"released\" event\n        // for either the left or right button, even if it wasn't pressed before pressing \"up\".\n        let x_axis_value = match dpad_value {\n            5..=7 => -1, // left\n            1..=3 => 1,  // right\n            _ => 0,\n        };\n        // Since we're emulating an inverted macOS gamepad axis, down is positive and up is negative\n        let y_axis_value = match dpad_value {\n            3..=5 => 1,      // down\n            0 | 1 | 7 => -1, // up\n            _ => 0,\n        };\n\n        let x_axis_event = Event::new(\n            id,\n            EventType::AxisValueChanged(\n                x_axis_value,\n                crate::EvCode(EvCode {\n                    page,\n                    usage: USAGE_AXIS_DPADX,\n                }),\n            ),\n        );\n        let y_axis_event = Event::new(\n            id,\n            EventType::AxisValueChanged(\n                y_axis_value,\n                crate::EvCode(EvCode {\n                    page,\n                    usage: USAGE_AXIS_DPADY,\n                }),\n            ),\n        );\n\n        let _ = tx.send((x_axis_event, None));\n        let _ = tx.send((y_axis_event, None));\n    }\n}\n"
  },
  {
    "path": "gilrs-core/src/platform/macos/io_kit.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\n#![allow(non_upper_case_globals)]\n\nuse objc2_core_foundation::{\n    kCFAllocatorDefault, CFArray, CFDictionary, CFNumber, CFRetained, CFString,\n    CFStringBuiltInEncodings, CFType,\n};\nuse objc2_io_kit::{\n    io_service_t, kHIDPage_Button, kHIDPage_Consumer, kHIDPage_GenericDesktop, kHIDPage_Simulation,\n    kHIDUsage_Button_1, kHIDUsage_GD_DPadDown, kHIDUsage_GD_DPadLeft, kHIDUsage_GD_DPadRight,\n    kHIDUsage_GD_DPadUp, kHIDUsage_GD_Dial, kHIDUsage_GD_GamePad, kHIDUsage_GD_Hatswitch,\n    kHIDUsage_GD_Joystick, kHIDUsage_GD_MultiAxisController, kHIDUsage_GD_Rx, kHIDUsage_GD_Ry,\n    kHIDUsage_GD_Rz, kHIDUsage_GD_Select, kHIDUsage_GD_Slider, kHIDUsage_GD_Start,\n    kHIDUsage_GD_SystemMainMenu, kHIDUsage_GD_Wheel, kHIDUsage_GD_X, kHIDUsage_GD_Y,\n    kHIDUsage_GD_Z, kHIDUsage_Sim_Accelerator, kHIDUsage_Sim_Brake, kHIDUsage_Sim_Rudder,\n    kHIDUsage_Sim_Throttle, kIOHIDDeviceUsageKey, kIOHIDDeviceUsagePageKey, kIOHIDLocationIDKey,\n    kIOHIDOptionsTypeNone, kIOHIDPrimaryUsageKey, kIOHIDPrimaryUsagePageKey, kIOHIDProductIDKey,\n    kIOHIDProductKey, kIOHIDVendorIDKey, kIOHIDVersionNumberKey, kIOReturnSuccess, IOHIDDevice,\n    IOHIDElement, IOHIDElementType, IOHIDManager, IOObjectRelease, IOObjectRetain,\n    IORegistryEntryGetRegistryEntryID, IO_OBJECT_NULL,\n};\n\nuse std::ffi::CStr;\n\npub fn new_manager() -> Option<CFRetained<IOHIDManager>> {\n    let manager = IOHIDManager::new(None, kIOHIDOptionsTypeNone);\n\n    let matchers = CFArray::from_retained_objects(&[\n        create_hid_device_matcher(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick),\n        create_hid_device_matcher(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad),\n        create_hid_device_matcher(kHIDPage_GenericDesktop, kHIDUsage_GD_MultiAxisController),\n    ]);\n\n    // SAFETY: The matchers are of the correct type.\n    unsafe { manager.set_device_matching_multiple(Some(matchers.as_opaque())) };\n\n    let ret = manager.open(kIOHIDOptionsTypeNone);\n    if ret != kIOReturnSuccess {\n        None\n    } else {\n        Some(manager)\n    }\n}\n\n#[derive(Debug, Clone)]\npub struct Device(pub CFRetained<IOHIDDevice>);\n\n// SAFETY: TODO, unsure?\nunsafe impl Sync for Device {}\nunsafe impl Send for Device {}\n\npub trait DeviceExt: Properties {\n    fn device(&self) -> &IOHIDDevice;\n\n    fn get_name(&self) -> Option<String> {\n        self.get_string_property(kIOHIDProductKey)\n            .map(|name| name.to_string())\n    }\n\n    fn get_location_id(&self) -> Option<u32> {\n        self.get_number_property(kIOHIDLocationIDKey)\n            .and_then(|location_id| location_id.as_i32().map(|location_id| location_id as u32))\n    }\n\n    fn get_vendor_id(&self) -> Option<u16> {\n        self.get_number_property(kIOHIDVendorIDKey)\n            .and_then(|vendor_id| vendor_id.as_i32().map(|vendor_id| vendor_id as u16))\n    }\n\n    fn get_product_id(&self) -> Option<u16> {\n        self.get_number_property(kIOHIDProductIDKey)\n            .and_then(|product_id| product_id.as_i32().map(|product_id| product_id as u16))\n    }\n\n    fn get_version(&self) -> Option<u16> {\n        self.get_number_property(kIOHIDVersionNumberKey)\n            .and_then(|version| version.as_i32().map(|version| version as u16))\n    }\n\n    fn get_page(&self) -> Option<u32> {\n        self.get_number_property(kIOHIDPrimaryUsagePageKey)\n            .and_then(|page| page.as_i32().map(|page| page as u32))\n    }\n\n    fn get_usage(&self) -> Option<u32> {\n        self.get_number_property(kIOHIDPrimaryUsageKey)\n            .and_then(|usage| usage.as_i32().map(|usage| usage as u32))\n    }\n\n    fn get_service(&self) -> Option<IOService> {\n        IOService::new(self.device().service())\n    }\n}\n\npub fn device_elements(device: &IOHIDDevice) -> Vec<CFRetained<IOHIDElement>> {\n    // SAFETY: We pass `None` as the dictionary, which means we don't have to worry about\n    // type-safety there.\n    let elements = unsafe { device.matching_elements(None, kIOHIDOptionsTypeNone) };\n\n    let Some(elements) = elements else {\n        return vec![];\n    };\n\n    // SAFETY: `IOHIDDeviceCopyMatchingElements` is documented to return CFArray of IOHIDElement.\n    let elements = unsafe { elements.cast_unchecked::<IOHIDElement>() };\n\n    elements.into_iter().collect()\n}\n\nimpl DeviceExt for IOHIDDevice {\n    fn device(&self) -> &IOHIDDevice {\n        self\n    }\n}\n\nimpl Properties for IOHIDDevice {\n    fn get_property(&self, key: &CStr) -> Option<CFRetained<CFType>> {\n        debug_assert!(key.to_str().is_ok());\n        // SAFETY: The key is a valid C string with UTF-8 contents.\n        let key = unsafe {\n            CFString::with_c_string(\n                kCFAllocatorDefault,\n                key.as_ptr(),\n                CFStringBuiltInEncodings::EncodingUTF8.0,\n            )?\n        };\n        self.property(&key)\n    }\n}\n\npub fn element_is_collection(type_: IOHIDElementType) -> bool {\n    type_ == IOHIDElementType::Collection\n}\n\npub fn element_is_axis(type_: IOHIDElementType, page: u32, usage: u32) -> bool {\n    match type_ {\n        IOHIDElementType::Input_Misc\n        | IOHIDElementType::Input_Button\n        | IOHIDElementType::Input_Axis => match page {\n            kHIDPage_GenericDesktop => {\n                matches!(\n                    usage,\n                    kHIDUsage_GD_X\n                        | kHIDUsage_GD_Y\n                        | kHIDUsage_GD_Z\n                        | kHIDUsage_GD_Rx\n                        | kHIDUsage_GD_Ry\n                        | kHIDUsage_GD_Rz\n                        | kHIDUsage_GD_Slider\n                        | kHIDUsage_GD_Dial\n                        | kHIDUsage_GD_Wheel\n                )\n            }\n            kHIDPage_Simulation => matches!(\n                usage,\n                kHIDUsage_Sim_Rudder\n                    | kHIDUsage_Sim_Throttle\n                    | kHIDUsage_Sim_Accelerator\n                    | kHIDUsage_Sim_Brake\n            ),\n            _ => false,\n        },\n        _ => false,\n    }\n}\n\npub fn element_is_button(type_: IOHIDElementType, page: u32, usage: u32) -> bool {\n    match type_ {\n        IOHIDElementType::Input_Misc\n        | IOHIDElementType::Input_Button\n        | IOHIDElementType::Input_Axis => match page {\n            kHIDPage_GenericDesktop => matches!(\n                usage,\n                kHIDUsage_GD_DPadUp\n                    | kHIDUsage_GD_DPadDown\n                    | kHIDUsage_GD_DPadRight\n                    | kHIDUsage_GD_DPadLeft\n                    | kHIDUsage_GD_Start\n                    | kHIDUsage_GD_Select\n                    | kHIDUsage_GD_SystemMainMenu\n            ),\n            kHIDPage_Button | kHIDPage_Consumer => true,\n            _ => false,\n        },\n        _ => false,\n    }\n}\n\npub fn element_is_hat(type_: IOHIDElementType, page: u32, usage: u32) -> bool {\n    match type_ {\n        IOHIDElementType::Input_Misc\n        | IOHIDElementType::Input_Button\n        | IOHIDElementType::Input_Axis => match page {\n            kHIDPage_GenericDesktop => matches!(usage, USAGE_AXIS_DPADX | USAGE_AXIS_DPADY),\n            _ => false,\n        },\n        _ => false,\n    }\n}\n\npub fn element_children(element: &IOHIDElement) -> Vec<CFRetained<IOHIDElement>> {\n    let elements = element.children();\n\n    let Some(elements) = elements else {\n        return vec![];\n    };\n\n    // SAFETY: `IOHIDElementGetChildren` is documented to return CFArray of IOHIDElement.\n    let elements = unsafe { elements.cast_unchecked::<IOHIDElement>() };\n\n    elements.into_iter().collect()\n}\n\nimpl Properties for IOHIDElement {\n    fn get_property(&self, key: &CStr) -> Option<CFRetained<CFType>> {\n        debug_assert!(key.to_str().is_ok());\n        // SAFETY: The key is a valid C string with UTF-8 contents.\n        let key = unsafe {\n            CFString::with_c_string(\n                kCFAllocatorDefault,\n                key.as_ptr(),\n                CFStringBuiltInEncodings::EncodingUTF8.0,\n            )?\n        };\n        self.property(&key)\n    }\n}\n\n#[repr(C)]\n#[derive(Debug)]\npub(crate) struct IOService(io_service_t);\n\nimpl IOService {\n    pub fn new(io_service: io_service_t) -> Option<IOService> {\n        if io_service == IO_OBJECT_NULL {\n            return None;\n        }\n\n        // We pair this retain with a release in `Drop`.\n        let result = IOObjectRetain(io_service);\n\n        if result == kIOReturnSuccess {\n            Some(IOService(io_service))\n        } else {\n            None\n        }\n    }\n\n    pub fn get_registry_entry_id(&self) -> Option<u64> {\n        IOObjectRetain(self.0);\n\n        let mut entry_id = 0;\n        // SAFETY: `&mut entry_id` is a valid pointer.\n        let result = unsafe { IORegistryEntryGetRegistryEntryID(self.0, &mut entry_id) };\n\n        IOObjectRelease(self.0);\n\n        if result == kIOReturnSuccess {\n            Some(entry_id)\n        } else {\n            None\n        }\n    }\n}\n\nimpl Drop for IOService {\n    fn drop(&mut self) {\n        IOObjectRelease(self.0 as _);\n    }\n}\n\npub trait Properties {\n    fn get_property(&self, key: &CStr) -> Option<CFRetained<CFType>>;\n\n    fn get_number_property(&self, key: &CStr) -> Option<CFRetained<CFNumber>> {\n        self.get_property(key)\n            .and_then(|value| value.downcast::<CFNumber>().ok())\n    }\n\n    fn get_string_property(&self, key: &CStr) -> Option<CFRetained<CFString>> {\n        self.get_property(key)\n            .and_then(|value| value.downcast::<CFString>().ok())\n    }\n}\n\nfn create_hid_device_matcher(\n    page: u32,\n    usage: u32,\n) -> CFRetained<CFDictionary<CFString, CFNumber>> {\n    let page_key = CFString::from_static_str(kIOHIDDeviceUsagePageKey.to_str().unwrap());\n    let page_value = CFNumber::new_i32(page as i32);\n\n    let usage_key = CFString::from_static_str(kIOHIDDeviceUsageKey.to_str().unwrap());\n    let usage_value = CFNumber::new_i32(usage as i32);\n\n    CFDictionary::from_slices(&[&*page_key, &*usage_key], &[&*page_value, &*usage_value])\n}\n\n// Revisions:\n// - MacOS Version: Sequoia 15.5 (Xbox One Elite Series 2 Controller) (20th of July 2025)\n\n// Usage Pages\npub const PAGE_GENERIC_DESKTOP: u32 = kHIDPage_GenericDesktop;\npub const PAGE_SIMULATION: u32 = kHIDPage_Simulation;\npub const PAGE_BUTTON: u32 = kHIDPage_Button;\n\n// GenericDesktop Page (0x01)\npub const USAGE_AXIS_LSTICKX: u32 = kHIDUsage_GD_X;\npub const USAGE_AXIS_LSTICKY: u32 = kHIDUsage_GD_Y;\npub const USAGE_AXIS_LEFTZ: u32 = 0; // unconfirmed\npub const USAGE_AXIS_RSTICKX: u32 = kHIDUsage_GD_Z;\npub const USAGE_AXIS_RSTICKY: u32 = kHIDUsage_GD_Rz;\npub const USAGE_AXIS_RIGHTZ: u32 = 0; // unconfirmed\npub const USAGE_AXIS_DPADX: u32 = kHIDUsage_GD_Hatswitch;\npub const USAGE_AXIS_DPADY: u32 = kHIDUsage_GD_Hatswitch + 1;\npub const USAGE_AXIS_RT: u32 = 0; // unconfirmed\npub const USAGE_AXIS_LT: u32 = 0; // unconfirmed\npub const USAGE_AXIS_RT2: u32 = kHIDUsage_Sim_Accelerator;\npub const USAGE_AXIS_LT2: u32 = kHIDUsage_Sim_Brake;\n\n// Button Page (0x09)\npub const USAGE_BTN_SOUTH: u32 = kHIDUsage_Button_1;\npub const USAGE_BTN_EAST: u32 = kHIDUsage_Button_1 + 1;\npub const USAGE_BTN_WEST: u32 = kHIDUsage_Button_1 + 3;\npub const USAGE_BTN_NORTH: u32 = kHIDUsage_Button_1 + 4;\npub const USAGE_BTN_LT: u32 = kHIDUsage_Button_1 + 6;\npub const USAGE_BTN_RT: u32 = kHIDUsage_Button_1 + 7;\npub const USAGE_BTN_LT2: u32 = kHIDUsage_Button_1 + 8; // unconfirmed\npub const USAGE_BTN_RT2: u32 = kHIDUsage_Button_1 + 9; // unconfirmed\npub const USAGE_BTN_SELECT: u32 = kHIDUsage_Button_1 + 10;\npub const USAGE_BTN_START: u32 = kHIDUsage_Button_1 + 11;\npub const USAGE_BTN_MODE: u32 = kHIDUsage_Button_1 + 12;\npub const USAGE_BTN_LTHUMB: u32 = kHIDUsage_Button_1 + 13;\npub const USAGE_BTN_RTHUMB: u32 = kHIDUsage_Button_1 + 14;\npub const USAGE_BTN_DPAD_UP: u32 = kHIDUsage_Button_1 + 15; // unconfirmed\npub const USAGE_BTN_DPAD_DOWN: u32 = kHIDUsage_Button_1 + 16; // unconfirmed\npub const USAGE_BTN_DPAD_LEFT: u32 = kHIDUsage_Button_1 + 17; // unconfirmed\npub const USAGE_BTN_DPAD_RIGHT: u32 = kHIDUsage_Button_1 + 18; // unconfirmed\npub const USAGE_BTN_C: u32 = kHIDUsage_Button_1 + 19; // unconfirmed\npub const USAGE_BTN_Z: u32 = kHIDUsage_Button_1 + 20; // unconfirmed\n"
  },
  {
    "path": "gilrs-core/src/platform/macos/mod.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\nmod ff;\nmod gamepad;\nmod io_kit;\n\npub use self::ff::Device as FfDevice;\npub use self::gamepad::{native_ev_codes, EvCode, Gamepad, Gilrs};\n\n// True, if Y axis of sticks points downwards.\npub const IS_Y_AXIS_REVERSED: bool = true;\n"
  },
  {
    "path": "gilrs-core/src/platform/mod.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\n\n//! Module which exports the platform-specific types.\n//!\n//! Each backend has to provide:\n//!\n//! * A `FfDevice` (a struct which handles force feedback)\n//! * A `Gilrs` context\n//! * A `Gamepad` struct\n//! * A static `str` which specifies the name of the SDL input mapping\n//! * A constant which define whether Y axis of sticks points upwards or downwards\n//! * A module with the platform-specific constants for common gamepad buttons\n//!   called `native_ev_codes`\n\n#![allow(clippy::module_inception)]\n\npub use self::platform::*;\n\n#[cfg(target_os = \"linux\")]\n#[path = \"linux/mod.rs\"]\nmod platform;\n\n#[cfg(target_os = \"macos\")]\n#[path = \"macos/mod.rs\"]\nmod platform;\n\n#[cfg(all(not(feature = \"xinput\"), not(feature = \"wgi\")))]\ncompile_error!(\n    \"Windows needs one of the features `gilrs/xinput` or `gilrs/wgi` enabled. \\nEither don't use \\\n     'default-features = false' or add one of the features back.\"\n);\n\n#[cfg(all(feature = \"wgi\", feature = \"xinput\"))]\ncompile_error!(\"features `gilrs/xinput` and `gilrs/wgi` are mutually exclusive\");\n\n#[cfg(all(target_os = \"windows\", feature = \"xinput\", not(feature = \"wgi\")))]\n#[path = \"windows_xinput/mod.rs\"]\nmod platform;\n\n#[cfg(all(target_os = \"windows\", feature = \"wgi\"))]\n#[path = \"windows_wgi/mod.rs\"]\nmod platform;\n\n#[cfg(target_arch = \"wasm32\")]\n#[path = \"wasm/mod.rs\"]\nmod platform;\n\n#[cfg(all(\n    not(any(target_os = \"linux\")),\n    not(target_os = \"macos\"),\n    not(target_os = \"windows\"),\n    not(target_arch = \"wasm32\")\n))]\n#[path = \"default/mod.rs\"]\nmod platform;\n"
  },
  {
    "path": "gilrs-core/src/platform/wasm/ff.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\n\nuse std::time::Duration;\n\n#[derive(Debug)]\npub struct Device;\n\nimpl Device {\n    pub fn set_ff_state(&mut self, _strong: u16, _weak: u16, _min_duration: Duration) {}\n}\n"
  },
  {
    "path": "gilrs-core/src/platform/wasm/gamepad.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\n\nuse std::collections::VecDeque;\nuse std::fmt::{Display, Formatter, Result as FmtResult};\nuse std::time::Duration;\n\nuse js_sys::RegExp;\nuse uuid::Uuid;\nuse wasm_bindgen::JsCast;\nuse web_sys::{DomException, Gamepad as WebGamepad, GamepadButton, GamepadMappingType};\n\nuse super::FfDevice;\nuse crate::platform::native_ev_codes::{BTN_LT2, BTN_RT2};\nuse crate::{AxisInfo, Event, EventType, PlatformError, PowerInfo};\n#[cfg(feature = \"serde-serialize\")]\nuse serde::{Deserialize, Serialize};\n\n#[derive(Debug)]\npub struct Gilrs {\n    event_cache: VecDeque<Event>,\n    gamepads: Vec<Gamepad>,\n    new_web_gamepads: Vec<WebGamepad>,\n    next_event_error_logged: bool,\n}\n\nimpl Gilrs {\n    pub(crate) fn new() -> Result<Self, PlatformError> {\n        let window =\n            web_sys::window().ok_or_else(|| PlatformError::Other(Box::new(Error::NoWindow)))?;\n        if !window.is_secure_context() {\n            warn!(\"Context is not secure, gamepad API may not be available.\")\n        }\n\n        Ok({\n            Gilrs {\n                event_cache: VecDeque::new(),\n                gamepads: Vec::new(),\n                new_web_gamepads: Vec::new(),\n                next_event_error_logged: false,\n            }\n        })\n    }\n\n    pub(crate) fn next_event(&mut self) -> Option<Event> {\n        // Don't duplicate the work of checking the diff between the old and new gamepads if\n        // there are still events to return\n        if !self.event_cache.is_empty() {\n            return self.event_cache.pop_front();\n        }\n\n        let gamepads = match web_sys::window()\n            .expect(\"no window\")\n            .navigator()\n            .get_gamepads()\n        {\n            Ok(x) => {\n                self.next_event_error_logged = false;\n                x\n            }\n            Err(js) => {\n                if !self.next_event_error_logged {\n                    self.next_event_error_logged = true;\n\n                    let exception: DomException = match js.dyn_into() {\n                        Ok(x) => x,\n                        Err(e) => {\n                            error!(\"getGamepads() failed with unknown error: {:?}\", e);\n                            return None;\n                        }\n                    };\n                    error!(\"getGamepads(): {}\", exception.message());\n                }\n\n                return None;\n            }\n        };\n\n        // Gather all non-null gamepads\n        for maybe_js_gamepad in gamepads {\n            if !maybe_js_gamepad.is_null() {\n                self.new_web_gamepads\n                    .push(WebGamepad::from(maybe_js_gamepad));\n            }\n        }\n\n        // Update existing gamepads\n        for (id, gamepad) in self.gamepads.iter_mut().enumerate() {\n            let maybe_js_gamepad_index = self\n                .new_web_gamepads\n                .iter()\n                .position(|x| gamepad.gamepad.index() == x.index());\n            if let Some(js_gamepad_index) = maybe_js_gamepad_index {\n                gamepad.gamepad = self.new_web_gamepads.swap_remove(js_gamepad_index);\n\n                if !gamepad.connected {\n                    self.event_cache\n                        .push_back(Event::new(id, EventType::Connected));\n                    gamepad.connected = true;\n                }\n\n                let buttons = gamepad.gamepad.buttons();\n                for btn_index in 0..gamepad\n                    .mapping\n                    .buttons()\n                    .len()\n                    .min(buttons.length() as usize)\n                {\n                    let (old_pressed, old_value) = gamepad.mapping.buttons()[btn_index];\n\n                    let ev_code = crate::EvCode(gamepad.button_code(btn_index));\n                    let button_object = GamepadButton::from(buttons.get(btn_index as u32));\n\n                    let new_pressed = button_object.pressed();\n                    let new_value = button_object.value();\n\n                    if [BTN_LT2, BTN_RT2].contains(&ev_code.0) && old_value != new_value {\n                        // Treat left and right triggers as axes so we get non-binary values.\n                        // Button Pressed/Changed events are generated from the axis changed\n                        // events later.\n                        let value = (new_value * i32::MAX as f64) as i32;\n                        self.event_cache\n                            .push_back(Event::new(id, EventType::AxisValueChanged(value, ev_code)));\n                    } else {\n                        match (old_pressed, new_pressed) {\n                            (false, true) => self\n                                .event_cache\n                                .push_back(Event::new(id, EventType::ButtonPressed(ev_code))),\n                            (true, false) => self\n                                .event_cache\n                                .push_back(Event::new(id, EventType::ButtonReleased(ev_code))),\n                            _ => (),\n                        }\n                    }\n\n                    gamepad.mapping.buttons_mut()[btn_index] = (new_pressed, new_value);\n                }\n\n                let axes = gamepad.gamepad.axes();\n                for axis_index in 0..gamepad.mapping.axes().len().min(axes.length() as usize) {\n                    let old_value = gamepad.mapping.axes()[axis_index];\n                    let new_value = axes\n                        .get(axis_index as u32)\n                        .as_f64()\n                        .expect(\"axes() should be an array of f64\");\n                    if old_value != new_value {\n                        let ev_code = crate::EvCode(gamepad.axis_code(axis_index));\n                        let value = (new_value * i32::MAX as f64) as i32;\n                        self.event_cache\n                            .push_back(Event::new(id, EventType::AxisValueChanged(value, ev_code)));\n                    }\n\n                    gamepad.mapping.axes_mut()[axis_index] = new_value;\n                }\n            } else {\n                // Create a disconnect event\n                if gamepad.connected {\n                    self.event_cache\n                        .push_back(Event::new(id, EventType::Disconnected));\n                    gamepad.connected = false;\n                }\n            }\n        }\n\n        // Add new gamepads\n        for js_gamepad in self.new_web_gamepads.drain(..) {\n            let id = self.gamepads.len();\n            self.gamepads.push(Gamepad::new(js_gamepad));\n\n            // Create a connected event\n            let event = Event::new(id, EventType::Connected);\n            self.event_cache.push_back(event);\n        }\n\n        self.event_cache.pop_front()\n    }\n\n    pub(crate) fn next_event_blocking(&mut self, _timeout: Option<Duration>) -> Option<Event> {\n        unimplemented!(\"next_event_blocking is not supported on web. Use next_event.\")\n    }\n\n    pub fn gamepad(&self, id: usize) -> Option<&Gamepad> {\n        self.gamepads.get(id)\n    }\n\n    pub fn last_gamepad_hint(&self) -> usize {\n        self.gamepads.len()\n    }\n}\n\n#[derive(Debug)]\n#[allow(clippy::large_enum_variant)]\nenum Mapping {\n    Standard {\n        buttons: [(bool, f64); 17],\n        axes: [f64; 4],\n    },\n    NoMapping {\n        buttons: Vec<(bool, f64)>,\n        axes: Vec<f64>,\n    },\n}\n\nimpl Mapping {\n    fn buttons(&self) -> &[(bool, f64)] {\n        match self {\n            Mapping::Standard { buttons, .. } => buttons,\n            Mapping::NoMapping { buttons, .. } => buttons,\n        }\n    }\n\n    fn buttons_mut(&mut self) -> &mut [(bool, f64)] {\n        match self {\n            Mapping::Standard { buttons, .. } => &mut *buttons,\n            Mapping::NoMapping { buttons, .. } => &mut *buttons,\n        }\n    }\n\n    fn axes(&self) -> &[f64] {\n        match self {\n            Mapping::Standard { axes, .. } => axes,\n            Mapping::NoMapping { axes, .. } => axes,\n        }\n    }\n\n    fn axes_mut(&mut self) -> &mut [f64] {\n        match self {\n            Mapping::Standard { axes, .. } => &mut *axes,\n            Mapping::NoMapping { axes, .. } => &mut *axes,\n        }\n    }\n}\n\n#[derive(Debug)]\npub struct Gamepad {\n    uuid: Uuid,\n    gamepad: WebGamepad,\n    name: String,\n    vendor: Option<u16>,\n    product: Option<u16>,\n    mapping: Mapping,\n    connected: bool,\n}\n\nimpl Gamepad {\n    fn new(gamepad: WebGamepad) -> Gamepad {\n        let name = gamepad.id();\n\n        // This regular expression extracts the vendor and product ID from the gamepad \"id\".\n        // Firefox:\n        //  054c-05c4-Sony Computer Entertainment Wireless Controller\n        // Chrome:\n        //  Sony Computer Entertainment Wireless Controller (STANDARD GAMEPAD Vendor: 054c Product: 05c4)\n        let regexp = RegExp::new(\n            r\"(?:^([a-f0-9]{4})-([a-f0-9]{4})-)|(?:Vendor: ([a-f0-9]{4}) Product: ([a-f0-9]{4})\\)$)\",\n            \"\",\n        );\n        let (vendor, product) = if let Some(matches) = regexp.exec(&name) {\n            let parse_hex = |index| {\n                matches\n                    .get(index)\n                    .as_string()\n                    .and_then(|id| u16::from_str_radix(&id, 16).ok())\n            };\n            (\n                parse_hex(1).or_else(|| parse_hex(3)),\n                parse_hex(2).or_else(|| parse_hex(4)),\n            )\n        } else {\n            (None, None)\n        };\n\n        let buttons = gamepad.buttons();\n        let button_iter = {\n            {\n                buttons.iter().map(GamepadButton::from)\n            }\n        };\n\n        let axes = gamepad.axes();\n        let axis_iter = {\n            {\n                axes.iter()\n                    .map(|val| val.as_f64().expect(\"axes() should be an array of f64\"))\n            }\n        };\n\n        let mapping = match gamepad.mapping() {\n            GamepadMappingType::Standard => {\n                let mut buttons = [(false, 0.0); 17];\n                let mut axes = [0.0; 4];\n\n                for (index, button) in button_iter.enumerate().take(buttons.len()) {\n                    buttons[index] = (button.pressed(), button.value());\n                }\n\n                for (index, axis) in axis_iter.enumerate().take(axes.len()) {\n                    axes[index] = axis;\n                }\n\n                Mapping::Standard { buttons, axes }\n            }\n            _ => {\n                let buttons = button_iter\n                    .map(|button| (button.pressed(), button.value()))\n                    .collect();\n                let axes = axis_iter.collect();\n                Mapping::NoMapping { buttons, axes }\n            }\n        };\n\n        Gamepad {\n            uuid: Uuid::nil(),\n            gamepad,\n            name,\n            vendor,\n            product,\n            mapping,\n            connected: true,\n        }\n    }\n\n    pub fn name(&self) -> &str {\n        &self.name\n    }\n\n    pub fn uuid(&self) -> Uuid {\n        self.uuid\n    }\n\n    pub fn vendor_id(&self) -> Option<u16> {\n        self.vendor\n    }\n\n    pub fn product_id(&self) -> Option<u16> {\n        self.product\n    }\n\n    pub fn is_connected(&self) -> bool {\n        self.gamepad.connected()\n    }\n\n    pub fn power_info(&self) -> PowerInfo {\n        PowerInfo::Unknown\n    }\n\n    pub fn is_ff_supported(&self) -> bool {\n        false\n    }\n\n    pub fn ff_device(&self) -> Option<FfDevice> {\n        None\n    }\n\n    pub fn buttons(&self) -> &[EvCode] {\n        &native_ev_codes::BUTTONS\n    }\n\n    pub fn axes(&self) -> &[EvCode] {\n        &native_ev_codes::AXES\n    }\n\n    fn button_code(&self, index: usize) -> EvCode {\n        self.buttons()\n            .get(index)\n            .copied()\n            .unwrap_or(EvCode(index as u8 + 31))\n    }\n\n    fn axis_code(&self, index: usize) -> EvCode {\n        self.axes()\n            .get(index)\n            .copied()\n            .unwrap_or_else(|| EvCode((index + self.mapping.buttons().len()) as u8 + 31))\n    }\n\n    pub(crate) fn axis_info(&self, _nec: EvCode) -> Option<&AxisInfo> {\n        if self.buttons().contains(&_nec) {\n            return Some(&AxisInfo {\n                min: 0,\n                max: i32::MAX,\n                deadzone: None,\n            });\n        }\n        Some(&AxisInfo {\n            min: i32::MIN,\n            max: i32::MAX,\n            deadzone: None,\n        })\n    }\n}\n\n#[cfg_attr(feature = \"serde-serialize\", derive(Serialize, Deserialize))]\n#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]\npub struct EvCode(u8);\n\nimpl EvCode {\n    pub fn into_u32(self) -> u32 {\n        self.0 as u32\n    }\n}\n\nimpl Display for EvCode {\n    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {\n        self.0.fmt(f)\n    }\n}\n\n#[derive(Debug, Copy, Clone)]\nenum Error {\n    NoWindow,\n}\n\nimpl Display for Error {\n    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {\n        match *self {\n            Error::NoWindow => f.write_str(\"window is not available\"),\n        }\n    }\n}\n\nimpl std::error::Error for Error {}\n\npub mod native_ev_codes {\n    use super::EvCode;\n\n    pub const AXIS_LSTICKX: EvCode = EvCode(0);\n    pub const AXIS_LSTICKY: EvCode = EvCode(1);\n    pub const AXIS_LEFTZ: EvCode = EvCode(2);\n    pub const AXIS_RSTICKX: EvCode = EvCode(3);\n    pub const AXIS_RSTICKY: EvCode = EvCode(4);\n    pub const AXIS_RIGHTZ: EvCode = EvCode(5);\n    pub const AXIS_DPADX: EvCode = EvCode(6);\n    pub const AXIS_DPADY: EvCode = EvCode(7);\n    pub const AXIS_RT: EvCode = EvCode(8);\n    pub const AXIS_LT: EvCode = EvCode(9);\n    pub const AXIS_RT2: EvCode = EvCode(10);\n    pub const AXIS_LT2: EvCode = EvCode(11);\n\n    pub const BTN_SOUTH: EvCode = EvCode(12);\n    pub const BTN_EAST: EvCode = EvCode(13);\n    pub const BTN_C: EvCode = EvCode(14);\n    pub const BTN_NORTH: EvCode = EvCode(15);\n    pub const BTN_WEST: EvCode = EvCode(16);\n    pub const BTN_Z: EvCode = EvCode(17);\n    pub const BTN_LT: EvCode = EvCode(18);\n    pub const BTN_RT: EvCode = EvCode(19);\n    pub const BTN_LT2: EvCode = EvCode(20);\n    pub const BTN_RT2: EvCode = EvCode(21);\n    pub const BTN_SELECT: EvCode = EvCode(22);\n    pub const BTN_START: EvCode = EvCode(23);\n    pub const BTN_MODE: EvCode = EvCode(24);\n    pub const BTN_LTHUMB: EvCode = EvCode(25);\n    pub const BTN_RTHUMB: EvCode = EvCode(26);\n\n    pub const BTN_DPAD_UP: EvCode = EvCode(27);\n    pub const BTN_DPAD_DOWN: EvCode = EvCode(28);\n    pub const BTN_DPAD_LEFT: EvCode = EvCode(29);\n    pub const BTN_DPAD_RIGHT: EvCode = EvCode(30);\n\n    pub(super) static BUTTONS: [EvCode; 17] = [\n        BTN_SOUTH,\n        BTN_EAST,\n        BTN_WEST,\n        BTN_NORTH,\n        BTN_LT,\n        BTN_RT,\n        BTN_LT2,\n        BTN_RT2,\n        BTN_SELECT,\n        BTN_START,\n        BTN_LTHUMB,\n        BTN_RTHUMB,\n        BTN_DPAD_UP,\n        BTN_DPAD_DOWN,\n        BTN_DPAD_LEFT,\n        BTN_DPAD_RIGHT,\n        BTN_MODE,\n    ];\n\n    pub(super) static AXES: [EvCode; 4] = [AXIS_LSTICKX, AXIS_LSTICKY, AXIS_RSTICKX, AXIS_RSTICKY];\n}\n"
  },
  {
    "path": "gilrs-core/src/platform/wasm/mod.rs",
    "content": "mod ff;\nmod gamepad;\n\npub use self::ff::Device as FfDevice;\npub use self::gamepad::{native_ev_codes, EvCode, Gamepad, Gilrs};\n\npub const IS_Y_AXIS_REVERSED: bool = true;\n"
  },
  {
    "path": "gilrs-core/src/platform/windows_wgi/ff.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\nuse std::time::Duration;\nuse windows::Gaming::Input::Gamepad as WgiGamepad;\nuse windows::Gaming::Input::GamepadVibration;\n\n#[derive(Debug)]\npub struct Device {\n    id: u32,\n    wgi_gamepad: Option<WgiGamepad>,\n}\n\nimpl Device {\n    pub(crate) fn new(id: u32, wgi_gamepad: Option<WgiGamepad>) -> Self {\n        Device { id, wgi_gamepad }\n    }\n\n    pub fn set_ff_state(&mut self, strong: u16, weak: u16, _min_duration: Duration) {\n        if let Some(wgi_gamepad) = &self.wgi_gamepad {\n            if let Err(err) = wgi_gamepad.SetVibration(GamepadVibration {\n                LeftMotor: (strong as f64) / (u16::MAX as f64),\n                RightMotor: (weak as f64) / (u16::MAX as f64),\n                LeftTrigger: 0.0,\n                RightTrigger: 0.0,\n            }) {\n                error!(\n                    \"Failed to change FF state – unknown error. ID = {}, error = {:?}.\",\n                    self.id, err\n                );\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "gilrs-core/src/platform/windows_wgi/gamepad.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\n\nuse super::FfDevice;\nuse crate::native_ev_codes as nec;\nuse crate::{utils, AxisInfo, Event, EventType, PlatformError, PowerInfo};\n\n#[cfg(feature = \"serde-serialize\")]\nuse serde::{Deserialize, Serialize};\nuse std::fmt::{Display, Formatter, Result as FmtResult};\nuse std::sync::mpsc::{self, Receiver, Sender, TryRecvError};\nuse std::thread;\nuse std::thread::JoinHandle;\nuse std::time::{Duration, Instant, SystemTime};\nuse uuid::Uuid;\nuse windows::core::HSTRING;\nuse windows::Devices::Power::BatteryReport;\nuse windows::Foundation::EventHandler;\nuse windows::Gaming::Input::{\n    GameControllerSwitchPosition, Gamepad as WgiGamepad, GamepadButtons, GamepadReading,\n    RawGameController,\n};\nuse windows::System::Power::BatteryStatus;\n\nconst SDL_HARDWARE_BUS_USB: u32 = 0x03;\n// const SDL_HARDWARE_BUS_BLUETOOTH: u32 = 0x05;\n\n// The general consensus is that standard xbox controllers poll at ~125 hz which\n// means 8 ms between updates.\n// Seems like a good target for how often we update the background thread.\nconst EVENT_THREAD_SLEEP_TIME: u64 = 8;\n\nconst WGI_TO_GILRS_BUTTON_MAP: [(GamepadButtons, crate::EvCode); 14] = [\n    (GamepadButtons::DPadUp, nec::BTN_DPAD_UP),\n    (GamepadButtons::DPadDown, nec::BTN_DPAD_DOWN),\n    (GamepadButtons::DPadLeft, nec::BTN_DPAD_LEFT),\n    (GamepadButtons::DPadRight, nec::BTN_DPAD_RIGHT),\n    (GamepadButtons::Menu, nec::BTN_START),\n    (GamepadButtons::View, nec::BTN_SELECT),\n    (GamepadButtons::LeftThumbstick, nec::BTN_LTHUMB),\n    (GamepadButtons::RightThumbstick, nec::BTN_RTHUMB),\n    (GamepadButtons::LeftShoulder, nec::BTN_LT),\n    (GamepadButtons::RightShoulder, nec::BTN_RT),\n    (GamepadButtons::A, nec::BTN_SOUTH),\n    (GamepadButtons::B, nec::BTN_EAST),\n    (GamepadButtons::X, nec::BTN_WEST),\n    (GamepadButtons::Y, nec::BTN_NORTH),\n];\n\n/// This is similar to `gilrs_core::Event` but has a raw_game_controller that still needs to be\n/// converted to a gilrs gamepad id.\n#[derive(Debug)]\nstruct WgiEvent {\n    raw_game_controller: RawGameController,\n    event: EventType,\n    pub time: SystemTime,\n}\n\nimpl WgiEvent {\n    fn new(raw_game_controller: RawGameController, event: EventType) -> Self {\n        let time = utils::time_now();\n        WgiEvent {\n            raw_game_controller,\n            event,\n            time,\n        }\n    }\n}\n\n#[derive(Debug)]\npub struct Gilrs {\n    gamepads: Vec<Gamepad>,\n    rx: Receiver<WgiEvent>,\n    join_handle: Option<JoinHandle<()>>,\n    stop_tx: Sender<()>,\n}\n\nimpl Gilrs {\n    pub(crate) fn new() -> Result<Self, PlatformError> {\n        let raw_game_controllers = RawGameController::RawGameControllers()\n            .map_err(|e| PlatformError::Other(Box::new(e)))?;\n        let count = raw_game_controllers\n            .Size()\n            .map_err(|e| PlatformError::Other(Box::new(e)))?;\n        // Intentionally avoiding using RawGameControllers.into_iter() as it triggers a crash when\n        // the app is run through steam.\n        // https://gitlab.com/gilrs-project/gilrs/-/issues/132\n        let gamepads = (0..count)\n            .map(|i| {\n                let controller = raw_game_controllers\n                    .GetAt(i)\n                    .map_err(|e| PlatformError::Other(Box::new(e)))?;\n                Ok(Gamepad::new(i, controller))\n            })\n            .collect::<Result<Vec<_>, _>>()?;\n\n        let (tx, rx) = mpsc::channel();\n        let (stop_tx, stop_rx) = mpsc::channel();\n        let join_handle = Some(Self::spawn_thread(tx, stop_rx));\n        Ok(Gilrs {\n            gamepads,\n            rx,\n            join_handle,\n            stop_tx,\n        })\n    }\n\n    fn spawn_thread(tx: Sender<WgiEvent>, stop_rx: Receiver<()>) -> JoinHandle<()> {\n        let added_tx = tx.clone();\n        let added_handler = EventHandler::<RawGameController>::new(move |_, g| {\n            if let Some(g) = g.as_ref() {\n                added_tx\n                    .send(WgiEvent::new(g.clone(), EventType::Connected))\n                    .expect(\"should be able to send to main thread\");\n            }\n            Ok(())\n        });\n        let controller_added_token =\n            RawGameController::RawGameControllerAdded(&added_handler).unwrap();\n\n        let removed_tx = tx.clone();\n        let removed_handler = EventHandler::<RawGameController>::new(move |_, g| {\n            if let Some(g) = g.as_ref() {\n                removed_tx\n                    .send(WgiEvent::new(g.clone(), EventType::Disconnected))\n                    .expect(\"should be able to send to main thread\");\n            }\n            Ok(())\n        });\n        let controller_removed_token =\n            RawGameController::RawGameControllerRemoved(&removed_handler).unwrap();\n\n        std::thread::Builder::new()\n            .name(\"gilrs\".to_owned())\n            .spawn(move || {\n                let mut controllers: Vec<RawGameController> = Vec::new();\n                // To avoid allocating every update, store old and new readings for every controller\n                // and swap their memory\n                let mut readings: Vec<(HSTRING, Reading, Reading)> = Vec::new();\n                let mut last_failed_get_id: Option<Instant> = None;\n                loop {\n                    match stop_rx.try_recv() {\n                        Ok(_) => break,\n                        Err(TryRecvError::Disconnected) => {\n                            warn!(\"stop_rx channel disconnected prematurely\");\n                            break;\n                        }\n                        Err(TryRecvError::Empty) => {}\n                    }\n                    controllers.clear();\n                    // Avoiding using RawGameControllers().into_iter() here due to it causing an\n                    // unhandled exception when the app is running through steam.\n                    // https://gitlab.com/gilrs-project/gilrs/-/issues/132\n                    if let Ok(raw_game_controllers) = RawGameController::RawGameControllers() {\n                        let count = raw_game_controllers.Size().unwrap_or_default();\n                        for index in 0..count {\n                            if let Ok(controller) = raw_game_controllers.GetAt(index) {\n                                controllers.push(controller);\n                            }\n                        }\n                    }\n\n                    for controller in controllers.iter() {\n                        let id: HSTRING = match controller.NonRoamableId() {\n                            Ok(id) => id,\n                            Err(e) => {\n                                if last_failed_get_id.map_or(true, |x| x.elapsed().as_secs() > 59) {\n                                    error!(\n                                        \"Failed to get gamepad id: {e}! Skipping reading events \\\n                                         for this gamepad.\"\n                                    );\n                                    last_failed_get_id = Some(Instant::now());\n                                }\n\n                                continue;\n                            }\n                        };\n                        // Find readings for this controller or insert new ones.\n                        let index = match readings.iter().position(|(other_id, ..)| id == *other_id)\n                        {\n                            None => {\n                                let reading = match WgiGamepad::FromGameController(controller) {\n                                    Ok(wgi_gamepad) => {\n                                        Reading::Gamepad(wgi_gamepad.GetCurrentReading().unwrap())\n                                    }\n                                    _ => Reading::Raw(RawGamepadReading::new(controller).unwrap()),\n                                };\n\n                                readings.push((id, reading.clone(), reading));\n                                readings.len() - 1\n                            }\n                            Some(i) => i,\n                        };\n\n                        let (_, old_reading, new_reading) = &mut readings[index];\n\n                        // Make last update's reading the old reading and get a new one.\n                        std::mem::swap(old_reading, new_reading);\n                        if let Err(e) = new_reading.update(controller) {\n                            if e.code().is_err() {\n                                error!(\"Reading::update() function failed with {e}\");\n                            }\n                        }\n\n                        // Skip if this is the same reading as the last one.\n                        if old_reading.time() == new_reading.time() {\n                            continue;\n                        }\n\n                        Reading::send_events_for_differences(\n                            old_reading,\n                            new_reading,\n                            controller,\n                            &tx,\n                        );\n                    }\n                    thread::sleep(Duration::from_millis(EVENT_THREAD_SLEEP_TIME));\n                }\n\n                if let Err(e) =\n                    RawGameController::RemoveRawGameControllerAdded(controller_added_token)\n                {\n                    error!(\"Failed to remove RawGameControllerAdded event handler: {e}\");\n                }\n\n                if let Err(e) =\n                    RawGameController::RemoveRawGameControllerRemoved(controller_removed_token)\n                {\n                    error!(\"Failed to remove RawGameControllerRemoved event handler: {e}\");\n                }\n            })\n            .expect(\"failed to spawn thread\")\n    }\n\n    pub(crate) fn next_event(&mut self) -> Option<Event> {\n        self.rx\n            .try_recv()\n            .ok()\n            .map(|wgi_event: WgiEvent| self.handle_event(wgi_event))\n    }\n\n    pub(crate) fn next_event_blocking(&mut self, timeout: Option<Duration>) -> Option<Event> {\n        if let Some(timeout) = timeout {\n            self.rx\n                .recv_timeout(timeout)\n                .ok()\n                .map(|wgi_event: WgiEvent| self.handle_event(wgi_event))\n        } else {\n            self.rx\n                .recv()\n                .ok()\n                .map(|wgi_event: WgiEvent| self.handle_event(wgi_event))\n        }\n    }\n\n    fn handle_event(&mut self, wgi_event: WgiEvent) -> Event {\n        // Find the index of the gamepad in our vec or insert it\n        let id = self\n            .gamepads\n            .iter()\n            .position(\n                |gamepad| match wgi_event.raw_game_controller.NonRoamableId() {\n                    Ok(id) => id == gamepad.non_roamable_id,\n                    _ => false,\n                },\n            )\n            .unwrap_or_else(|| {\n                self.gamepads.push(Gamepad::new(\n                    self.gamepads.len() as u32,\n                    wgi_event.raw_game_controller,\n                ));\n                self.gamepads.len() - 1\n            });\n\n        match wgi_event.event {\n            EventType::Connected => self.gamepads[id].is_connected = true,\n            EventType::Disconnected => self.gamepads[id].is_connected = false,\n            _ => (),\n        }\n        Event {\n            id,\n            event: wgi_event.event,\n            time: wgi_event.time,\n        }\n    }\n\n    pub fn gamepad(&self, id: usize) -> Option<&Gamepad> {\n        self.gamepads.get(id)\n    }\n\n    pub fn last_gamepad_hint(&self) -> usize {\n        self.gamepads.len()\n    }\n}\n\nimpl Drop for Gilrs {\n    fn drop(&mut self) {\n        if let Err(e) = self.stop_tx.send(()) {\n            warn!(\"Failed to send stop signal to thread: {e:?}\");\n        }\n        if let Err(e) = self.join_handle.take().unwrap().join() {\n            warn!(\"Failed to join thread: {e:?}\");\n        }\n    }\n}\n\n#[derive(Debug, Clone)]\nstruct RawGamepadReading {\n    axes: Vec<f64>,\n    buttons: Vec<bool>,\n    switches: Vec<GameControllerSwitchPosition>,\n    time: u64,\n}\n\nimpl RawGamepadReading {\n    fn new(raw_game_controller: &RawGameController) -> windows::core::Result<Self> {\n        let axis_count = raw_game_controller.AxisCount()? as usize;\n        let button_count = raw_game_controller.ButtonCount()? as usize;\n        let switch_count = raw_game_controller.SwitchCount()? as usize;\n        let mut new = Self {\n            axes: vec![0.0; axis_count],\n            buttons: vec![false; button_count],\n            switches: vec![GameControllerSwitchPosition::default(); switch_count],\n            time: 0,\n        };\n        new.time = raw_game_controller.GetCurrentReading(\n            &mut new.buttons,\n            &mut new.switches,\n            &mut new.axes,\n        )?;\n        Ok(new)\n    }\n\n    fn update(&mut self, raw_game_controller: &RawGameController) -> windows::core::Result<()> {\n        self.time = raw_game_controller.GetCurrentReading(\n            &mut self.buttons,\n            &mut self.switches,\n            &mut self.axes,\n        )?;\n        Ok(())\n    }\n}\n\n/// Treats switches like a two axes similar to a Directional pad.\n/// Returns a tuple containing the values of the x and y axis.\n/// Value's range is -1 to 1.\nfn direction_from_switch(switch: GameControllerSwitchPosition) -> (i32, i32) {\n    match switch {\n        GameControllerSwitchPosition::Up => (0, 1),\n        GameControllerSwitchPosition::Down => (0, -1),\n        GameControllerSwitchPosition::Right => (1, 0),\n        GameControllerSwitchPosition::Left => (-1, 0),\n        GameControllerSwitchPosition::UpLeft => (-1, 1),\n        GameControllerSwitchPosition::UpRight => (1, 1),\n        GameControllerSwitchPosition::DownLeft => (-1, -1),\n        GameControllerSwitchPosition::DownRight => (1, -1),\n        _ => (0, 0),\n    }\n}\n\n#[derive(Clone)]\nenum Reading {\n    Raw(RawGamepadReading),\n    Gamepad(GamepadReading),\n}\n\nimpl Reading {\n    fn time(&self) -> u64 {\n        match self {\n            Reading::Raw(r) => r.time,\n            Reading::Gamepad(r) => r.Timestamp,\n        }\n    }\n\n    fn update(&mut self, controller: &RawGameController) -> windows::core::Result<()> {\n        match self {\n            Reading::Raw(raw_reading) => {\n                raw_reading.update(controller)?;\n            }\n            Reading::Gamepad(gamepad_reading) => {\n                let gamepad: WgiGamepad = WgiGamepad::FromGameController(controller)?;\n                *gamepad_reading = gamepad.GetCurrentReading()?;\n            }\n        }\n        Ok(())\n    }\n\n    fn send_events_for_differences(\n        old: &Self,\n        new: &Self,\n        controller: &RawGameController,\n        tx: &Sender<WgiEvent>,\n    ) {\n        match (old, new) {\n            // WGI RawGameController\n            (Reading::Raw(old), Reading::Raw(new)) => {\n                // Axis changes\n                for index in 0..new.axes.len() {\n                    if old.axes.get(index) != new.axes.get(index) {\n                        // https://github.com/libsdl-org/SDL/blob/6af17369ca773155bd7f39b8801725c4a6d52e4f/src/joystick/windows/SDL_windows_gaming_input.c#L863\n                        let value = ((new.axes[index] * 65535.0) - 32768.0) as i32;\n                        let event_type = EventType::AxisValueChanged(\n                            value,\n                            crate::EvCode(EvCode {\n                                kind: EvCodeKind::Axis,\n                                index: index as u32,\n                            }),\n                        );\n                        tx.send(WgiEvent::new(controller.clone(), event_type))\n                            .unwrap()\n                    }\n                }\n                for index in 0..new.buttons.len() {\n                    if old.buttons.get(index) != new.buttons.get(index) {\n                        let event_type = match new.buttons[index] {\n                            true => EventType::ButtonPressed(crate::EvCode(EvCode {\n                                kind: EvCodeKind::Button,\n                                index: index as u32,\n                            })),\n                            false => EventType::ButtonReleased(crate::EvCode(EvCode {\n                                kind: EvCodeKind::Button,\n                                index: index as u32,\n                            })),\n                        };\n                        tx.send(WgiEvent::new(controller.clone(), event_type))\n                            .unwrap()\n                    }\n                }\n\n                for index in 0..old.switches.len() {\n                    let (old_x, old_y) = direction_from_switch(old.switches[index]);\n                    let (new_x, new_y) = direction_from_switch(new.switches[index]);\n                    if old_x != new_x {\n                        let event_type = EventType::AxisValueChanged(\n                            new_x,\n                            crate::EvCode(EvCode {\n                                kind: EvCodeKind::Switch,\n                                index: (index * 2) as u32,\n                            }),\n                        );\n                        tx.send(WgiEvent::new(controller.clone(), event_type))\n                            .unwrap()\n                    }\n                    if old_y != new_y {\n                        let event_type = EventType::AxisValueChanged(\n                            -new_y,\n                            crate::EvCode(EvCode {\n                                kind: EvCodeKind::Switch,\n                                index: (index * 2) as u32 + 1,\n                            }),\n                        );\n                        tx.send(WgiEvent::new(controller.clone(), event_type))\n                            .unwrap()\n                    }\n                }\n            }\n            // WGI Gamepad\n            (Reading::Gamepad(old), Reading::Gamepad(new)) => {\n                #[rustfmt::skip]\n                let axes = [\n                    (new.LeftTrigger, old.LeftTrigger, nec::AXIS_LT2, 1.0),\n                    (new.RightTrigger, old.RightTrigger, nec::AXIS_RT2, 1.0),\n                    (new.LeftThumbstickX, old.LeftThumbstickX, nec::AXIS_LSTICKX, 1.0),\n                    (new.LeftThumbstickY, old.LeftThumbstickY, nec::AXIS_LSTICKY, -1.0),\n                    (new.RightThumbstickX, old.RightThumbstickX, nec::AXIS_RSTICKX, 1.0),\n                    (new.RightThumbstickY, old.RightThumbstickY, nec::AXIS_RSTICKY, -1.0),\n                ];\n                for (new, old, code, multiplier) in axes {\n                    if new != old {\n                        let _ = tx.send(WgiEvent::new(\n                            controller.clone(),\n                            EventType::AxisValueChanged(\n                                (multiplier * new * i32::MAX as f64) as i32,\n                                code,\n                            ),\n                        ));\n                    }\n                }\n\n                for (current_button, ev_code) in WGI_TO_GILRS_BUTTON_MAP {\n                    if (new.Buttons & current_button) != (old.Buttons & current_button) {\n                        let _ = match new.Buttons & current_button != GamepadButtons::None {\n                            true => tx.send(WgiEvent::new(\n                                controller.clone(),\n                                EventType::ButtonPressed(ev_code),\n                            )),\n                            false => tx.send(WgiEvent::new(\n                                controller.clone(),\n                                EventType::ButtonReleased(ev_code),\n                            )),\n                        };\n                    }\n                }\n            }\n            (a, b) => {\n                warn!(\n                    \"WGI Controller changed from gamepad: {} to gamepad: {}. Could not compare \\\n                     last update.\",\n                    a.is_gamepad(),\n                    b.is_gamepad()\n                );\n                #[cfg(debug_assertions)]\n                panic!(\n                    \"Controllers shouldn't change type between updates, likely programmer error\"\n                );\n            }\n        }\n    }\n\n    fn is_gamepad(&self) -> bool {\n        matches!(self, Reading::Gamepad(_))\n    }\n}\n\n#[derive(Debug)]\npub struct Gamepad {\n    id: u32,\n    name: String,\n    uuid: Uuid,\n    is_connected: bool,\n    /// This is the generic controller handle without any mappings\n    /// https://learn.microsoft.com/en-us/uwp/api/windows.gaming.input.rawgamecontroller\n    raw_game_controller: RawGameController,\n    /// An ID for this device that will survive disconnects and restarts.\n    /// [NonRoamableIds](https://learn.microsoft.com/en-us/uwp/api/windows.gaming.input.rawgamecontroller.nonroamableid)\n    ///\n    /// Changes if plugged into a different port and is not the same between different applications\n    /// or PCs.\n    non_roamable_id: HSTRING,\n    /// If the controller has a [Gamepad](https://learn.microsoft.com/en-us/uwp/api/windows.gaming.input.gamepad?view=winrt-22621)\n    /// mapping, this is used to access the mapped values.\n    wgi_gamepad: Option<WgiGamepad>,\n    axes: Option<Vec<EvCode>>,\n    buttons: Option<Vec<EvCode>>,\n}\n\nimpl Gamepad {\n    fn new(id: u32, raw_game_controller: RawGameController) -> Gamepad {\n        let is_connected = true;\n\n        let non_roamable_id = raw_game_controller.NonRoamableId().unwrap();\n\n        // See if we can cast this to a windows definition of a gamepad\n        let wgi_gamepad = WgiGamepad::FromGameController(&raw_game_controller).ok();\n        let name = match raw_game_controller.DisplayName() {\n            Ok(hstring) => hstring.to_string_lossy(),\n            Err(_) => \"unknown\".to_string(),\n        };\n\n        let uuid = match wgi_gamepad.is_some() {\n            true => Uuid::nil(),\n            false => {\n                let vendor_id = raw_game_controller.HardwareVendorId().unwrap_or(0).to_be();\n                let product_id = raw_game_controller.HardwareProductId().unwrap_or(0).to_be();\n                let version = 0;\n\n                // SDL uses the SDL_HARDWARE_BUS_BLUETOOTH bustype for IsWireless devices:\n                // https://github.com/libsdl-org/SDL/blob/294ccba0a23b37fffef62189423444f93732e565/src/joystick/windows/SDL_windows_gaming_input.c#L335-L338\n                // In my testing though, it caused my controllers to not find mappings.\n                // SDL only uses their WGI implementation for UWP apps so I guess it hasn't been\n                // used enough for people to submit mappings with the different bustype.\n                let bustype = SDL_HARDWARE_BUS_USB.to_be();\n\n                Uuid::from_fields(\n                    bustype,\n                    vendor_id,\n                    0,\n                    &[\n                        (product_id >> 8) as u8,\n                        product_id as u8,\n                        0,\n                        0,\n                        (version >> 8) as u8,\n                        version as u8,\n                        0,\n                        0,\n                    ],\n                )\n            }\n        };\n\n        let mut gamepad = Gamepad {\n            id,\n            name,\n            uuid,\n            is_connected,\n            raw_game_controller,\n            non_roamable_id,\n            wgi_gamepad,\n            axes: None,\n            buttons: None,\n        };\n\n        if gamepad.wgi_gamepad.is_none() {\n            gamepad.collect_axes_and_buttons();\n        }\n\n        gamepad\n    }\n\n    pub fn name(&self) -> &str {\n        &self.name\n    }\n\n    pub fn uuid(&self) -> Uuid {\n        self.uuid\n    }\n\n    pub fn vendor_id(&self) -> Option<u16> {\n        self.raw_game_controller.HardwareVendorId().ok()\n    }\n\n    pub fn product_id(&self) -> Option<u16> {\n        self.raw_game_controller.HardwareProductId().ok()\n    }\n\n    pub fn is_connected(&self) -> bool {\n        self.is_connected\n    }\n\n    pub fn power_info(&self) -> PowerInfo {\n        self.power_info_err().unwrap_or(PowerInfo::Unknown)\n    }\n\n    /// Using this function so we can easily map errors to unknown\n    fn power_info_err(&self) -> windows::core::Result<PowerInfo> {\n        if !self.raw_game_controller.IsWireless()? {\n            return Ok(PowerInfo::Wired);\n        }\n        let report: BatteryReport = self.raw_game_controller.TryGetBatteryReport()?;\n        let status: BatteryStatus = report.Status()?;\n\n        let power_info = match status {\n            BatteryStatus::Discharging | BatteryStatus::Charging => {\n                let full = report.FullChargeCapacityInMilliwattHours()?.GetInt32()? as f32;\n                let remaining = report.RemainingCapacityInMilliwattHours()?.GetInt32()? as f32;\n                let percent: u8 = ((remaining / full) * 100.0) as u8;\n                match status {\n                    _ if percent == 100 => PowerInfo::Charged,\n                    BatteryStatus::Discharging => PowerInfo::Discharging(percent),\n                    BatteryStatus::Charging => PowerInfo::Charging(percent),\n                    _ => unreachable!(),\n                }\n            }\n            BatteryStatus::NotPresent => PowerInfo::Wired,\n            BatteryStatus::Idle => PowerInfo::Charged,\n            BatteryStatus(_) => PowerInfo::Unknown,\n        };\n        Ok(power_info)\n    }\n\n    pub fn is_ff_supported(&self) -> bool {\n        self.wgi_gamepad.is_some()\n            && self\n                .raw_game_controller\n                .ForceFeedbackMotors()\n                .ok()\n                .map(|motors| motors.First())\n                .is_some()\n    }\n\n    pub fn ff_device(&self) -> Option<FfDevice> {\n        Some(FfDevice::new(self.id, self.wgi_gamepad.clone()))\n    }\n\n    pub fn buttons(&self) -> &[EvCode] {\n        match &self.buttons {\n            None => &native_ev_codes::BUTTONS,\n            Some(buttons) => buttons,\n        }\n    }\n\n    pub fn axes(&self) -> &[EvCode] {\n        match &self.axes {\n            None => &native_ev_codes::AXES,\n            Some(axes) => axes,\n        }\n    }\n\n    pub(crate) fn axis_info(&self, nec: EvCode) -> Option<&AxisInfo> {\n        // If it isn't a Windows \"Gamepad\" then return what we want SDL mappings to be able to use\n        if self.wgi_gamepad.is_none() {\n            return match nec.kind {\n                EvCodeKind::Button => None,\n                EvCodeKind::Axis => Some(&AxisInfo {\n                    min: i16::MIN as i32,\n                    max: i16::MAX as i32,\n                    deadzone: None,\n                }),\n                EvCodeKind::Switch => Some(&AxisInfo {\n                    min: -1,\n                    max: 1,\n                    deadzone: None,\n                }),\n            };\n        }\n\n        // For Windows Gamepads, the triggers are 0.0 to 1.0 and the thumbsticks are -1.0 to 1.0\n        // https://learn.microsoft.com/en-us/uwp/api/windows.gaming.input.gamepadreading#fields\n        // Since Gilrs processes axis data as integers, the input has already been multiplied by\n        // i32::MAX in the joy_value method.\n        match nec {\n            native_ev_codes::AXIS_LT2 | native_ev_codes::AXIS_RT2 => Some(&AxisInfo {\n                min: 0,\n                max: i32::MAX,\n                deadzone: None,\n            }),\n            _ => Some(&AxisInfo {\n                min: i32::MIN,\n                max: i32::MAX,\n                deadzone: None,\n            }),\n        }\n    }\n\n    fn collect_axes_and_buttons(&mut self) {\n        let axis_count = self.raw_game_controller.AxisCount().unwrap() as u32;\n        let button_count = self.raw_game_controller.ButtonCount().unwrap() as u32;\n        let switch_count = self.raw_game_controller.SwitchCount().unwrap() as u32;\n        self.buttons = Some(\n            (0..button_count)\n                .map(|index| EvCode {\n                    kind: EvCodeKind::Button,\n                    index,\n                })\n                .collect(),\n        );\n        self.axes = Some(\n            (0..axis_count)\n                .map(|index| EvCode {\n                    kind: EvCodeKind::Axis,\n                    index,\n                })\n                .chain(\n                    // Treat switches as two axes\n                    (0..switch_count).flat_map(|index| {\n                        [\n                            EvCode {\n                                kind: EvCodeKind::Switch,\n                                index: index * 2,\n                            },\n                            EvCode {\n                                kind: EvCodeKind::Switch,\n                                index: (index * 2) + 1,\n                            },\n                        ]\n                    }),\n                )\n                .collect(),\n        );\n    }\n}\n\n#[cfg_attr(feature = \"serde-serialize\", derive(Serialize, Deserialize))]\n#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]\nenum EvCodeKind {\n    Button = 0,\n    Axis,\n    Switch,\n}\n\nimpl Display for EvCodeKind {\n    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {\n        match self {\n            EvCodeKind::Button => \"Button\",\n            EvCodeKind::Axis => \"Axis\",\n            EvCodeKind::Switch => \"Switch\",\n        }\n        .fmt(f)\n    }\n}\n\n#[cfg_attr(feature = \"serde-serialize\", derive(Serialize, Deserialize))]\n#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]\npub struct EvCode {\n    kind: EvCodeKind,\n    index: u32,\n}\n\nimpl EvCode {\n    pub fn into_u32(self) -> u32 {\n        ((self.kind as u32) << 16) | self.index\n    }\n}\n\nimpl Display for EvCode {\n    fn fmt(&self, f: &mut Formatter) -> FmtResult {\n        write!(f, \"{}({})\", self.kind, self.index)\n    }\n}\n\npub mod native_ev_codes {\n    use super::{EvCode, EvCodeKind};\n\n    pub const AXIS_LSTICKX: EvCode = EvCode {\n        kind: EvCodeKind::Axis,\n        index: 0,\n    };\n    pub const AXIS_LSTICKY: EvCode = EvCode {\n        kind: EvCodeKind::Axis,\n        index: 1,\n    };\n    pub const AXIS_RSTICKX: EvCode = EvCode {\n        kind: EvCodeKind::Axis,\n        index: 2,\n    };\n    pub const AXIS_LT2: EvCode = EvCode {\n        kind: EvCodeKind::Axis,\n        index: 3,\n    };\n    pub const AXIS_RT2: EvCode = EvCode {\n        kind: EvCodeKind::Axis,\n        index: 4,\n    };\n    pub const AXIS_RSTICKY: EvCode = EvCode {\n        kind: EvCodeKind::Axis,\n        index: 5,\n    };\n    pub const AXIS_RT: EvCode = EvCode {\n        kind: EvCodeKind::Axis,\n        index: 6,\n    };\n    pub const AXIS_LT: EvCode = EvCode {\n        kind: EvCodeKind::Axis,\n        index: 7,\n    };\n    pub const AXIS_LEFTZ: EvCode = EvCode {\n        kind: EvCodeKind::Axis,\n        index: 8,\n    };\n    pub const AXIS_RIGHTZ: EvCode = EvCode {\n        kind: EvCodeKind::Axis,\n        index: 9,\n    };\n\n    pub const AXIS_DPADX: EvCode = EvCode {\n        kind: EvCodeKind::Switch,\n        index: 0,\n    };\n    pub const AXIS_DPADY: EvCode = EvCode {\n        kind: EvCodeKind::Switch,\n        index: 1,\n    };\n\n    pub const BTN_WEST: EvCode = EvCode {\n        kind: EvCodeKind::Button,\n        index: 0,\n    };\n    pub const BTN_SOUTH: EvCode = EvCode {\n        kind: EvCodeKind::Button,\n        index: 1,\n    };\n    pub const BTN_EAST: EvCode = EvCode {\n        kind: EvCodeKind::Button,\n        index: 2,\n    };\n    pub const BTN_NORTH: EvCode = EvCode {\n        kind: EvCodeKind::Button,\n        index: 3,\n    };\n    pub const BTN_LT: EvCode = EvCode {\n        kind: EvCodeKind::Button,\n        index: 4,\n    };\n    pub const BTN_RT: EvCode = EvCode {\n        kind: EvCodeKind::Button,\n        index: 5,\n    };\n    pub const BTN_LT2: EvCode = EvCode {\n        kind: EvCodeKind::Button,\n        index: 6,\n    };\n    pub const BTN_RT2: EvCode = EvCode {\n        kind: EvCodeKind::Button,\n        index: 7,\n    };\n    pub const BTN_SELECT: EvCode = EvCode {\n        kind: EvCodeKind::Button,\n        index: 8,\n    };\n    pub const BTN_START: EvCode = EvCode {\n        kind: EvCodeKind::Button,\n        index: 9,\n    };\n    pub const BTN_LTHUMB: EvCode = EvCode {\n        kind: EvCodeKind::Button,\n        index: 10,\n    };\n    pub const BTN_RTHUMB: EvCode = EvCode {\n        kind: EvCodeKind::Button,\n        index: 11,\n    };\n    pub const BTN_MODE: EvCode = EvCode {\n        kind: EvCodeKind::Button,\n        index: 12,\n    };\n    pub const BTN_C: EvCode = EvCode {\n        kind: EvCodeKind::Button,\n        index: 13,\n    };\n    pub const BTN_Z: EvCode = EvCode {\n        kind: EvCodeKind::Button,\n        index: 14,\n    };\n\n    // The DPad for DS4 controllers is a hat/switch that gets mapped to the DPad native event\n    // code buttons. These \"buttons\" don't exist on the DS4 controller, so it doesn't matter\n    // what the index is, but if it overlaps with an existing button it will send the event\n    // for the overlapping button as a dpad button instead of unknown.\n    // By using a large index it should avoid this.\n    pub const BTN_DPAD_UP: EvCode = EvCode {\n        kind: EvCodeKind::Button,\n        index: u32::MAX - 3,\n    };\n    pub const BTN_DPAD_RIGHT: EvCode = EvCode {\n        kind: EvCodeKind::Button,\n        index: u32::MAX - 2,\n    };\n    pub const BTN_DPAD_DOWN: EvCode = EvCode {\n        kind: EvCodeKind::Button,\n        index: u32::MAX - 1,\n    };\n    pub const BTN_DPAD_LEFT: EvCode = EvCode {\n        kind: EvCodeKind::Button,\n        index: u32::MAX,\n    };\n\n    pub(super) static BUTTONS: [EvCode; 14] = [\n        BTN_WEST,\n        BTN_SOUTH,\n        BTN_EAST,\n        BTN_NORTH,\n        BTN_LT,\n        BTN_RT,\n        BTN_SELECT,\n        BTN_START,\n        BTN_LTHUMB,\n        BTN_RTHUMB,\n        BTN_DPAD_UP,\n        BTN_DPAD_RIGHT,\n        BTN_DPAD_DOWN,\n        BTN_DPAD_LEFT,\n    ];\n\n    pub(super) static AXES: [EvCode; 6] = [\n        AXIS_LSTICKX,\n        AXIS_LSTICKY,\n        AXIS_RSTICKX,\n        AXIS_LT2,\n        AXIS_RT2,\n        AXIS_RSTICKY,\n    ];\n}\n"
  },
  {
    "path": "gilrs-core/src/platform/windows_wgi/mod.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\nmod ff;\nmod gamepad;\n\npub use self::ff::Device as FfDevice;\npub use self::gamepad::{native_ev_codes, EvCode, Gamepad, Gilrs};\n\npub const IS_Y_AXIS_REVERSED: bool = true;\n"
  },
  {
    "path": "gilrs-core/src/platform/windows_xinput/ff.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\n\nuse rusty_xinput::{self, XInputHandle, XInputUsageError};\nuse std::sync::Arc;\nuse std::time::Duration;\n\n#[derive(Debug)]\npub struct Device {\n    id: u32,\n    xinput_handle: Arc<XInputHandle>,\n}\n\nimpl Device {\n    pub(crate) fn new(id: u32, xinput_handle: Arc<XInputHandle>) -> Self {\n        Device { id, xinput_handle }\n    }\n\n    pub fn set_ff_state(&mut self, strong: u16, weak: u16, _min_duration: Duration) {\n        match self.xinput_handle.set_state(self.id, strong, weak) {\n            Ok(()) => (),\n            Err(XInputUsageError::DeviceNotConnected) => {\n                error!(\n                    \"Failed to change FF state – gamepad with id {} is no longer connected.\",\n                    self.id\n                );\n            }\n            Err(err) => {\n                error!(\n                    \"Failed to change FF state – unknown error. ID = {}, error = {:?}.\",\n                    self.id, err\n                );\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "gilrs-core/src/platform/windows_xinput/gamepad.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\n\nuse super::FfDevice;\nuse crate::{AxisInfo, Event, EventType, PlatformError, PowerInfo};\n\nuse std::error::Error as StdError;\nuse std::fmt::{Display, Formatter, Result as FmtResult};\nuse std::sync::{\n    mpsc::{self, Receiver, Sender},\n    Arc,\n};\nuse std::time::Duration;\nuse std::{mem, thread};\n\nuse rusty_xinput::{\n    BatteryLevel, BatteryType, XInputHandle, XInputLoadingFailure, XInputState, XInputUsageError,\n};\nuse uuid::Uuid;\nuse winapi::um::xinput::{\n    XINPUT_GAMEPAD as XGamepad, XINPUT_GAMEPAD_A, XINPUT_GAMEPAD_B, XINPUT_GAMEPAD_BACK,\n    XINPUT_GAMEPAD_DPAD_DOWN, XINPUT_GAMEPAD_DPAD_LEFT, XINPUT_GAMEPAD_DPAD_RIGHT,\n    XINPUT_GAMEPAD_DPAD_UP, XINPUT_GAMEPAD_LEFT_SHOULDER, XINPUT_GAMEPAD_LEFT_THUMB,\n    XINPUT_GAMEPAD_RIGHT_SHOULDER, XINPUT_GAMEPAD_RIGHT_THUMB, XINPUT_GAMEPAD_START,\n    XINPUT_GAMEPAD_X, XINPUT_GAMEPAD_Y, XINPUT_STATE as XState,\n};\n\n// Chosen by dice roll ;)\nconst EVENT_THREAD_SLEEP_TIME: u64 = 10;\nconst ITERATIONS_TO_CHECK_IF_CONNECTED: u64 = 100;\n\nconst MAX_XINPUT_CONTROLLERS: usize = 4;\n\n#[derive(Debug)]\npub struct Gilrs {\n    gamepads: [Gamepad; MAX_XINPUT_CONTROLLERS],\n    rx: Receiver<Event>,\n}\n\nimpl Gilrs {\n    pub(crate) fn new() -> Result<Self, PlatformError> {\n        let xinput_handle = XInputHandle::load_default()\n            .map_err(|e| PlatformError::Other(Box::new(Error::FailedToLoadDll(e))))?;\n        let xinput_handle = Arc::new(xinput_handle);\n\n        let gamepad_ids: [usize; MAX_XINPUT_CONTROLLERS] = std::array::from_fn(|idx| idx);\n\n        // Map controller IDs to Gamepads\n        let gamepads = gamepad_ids.map(|id| Gamepad::new(id as u32, xinput_handle.clone()));\n\n        let mut connected: [bool; MAX_XINPUT_CONTROLLERS] = Default::default();\n\n        // Iterate through each controller ID and set connected state\n        for id in 0..MAX_XINPUT_CONTROLLERS {\n            connected[id] = gamepads[id].is_connected;\n        }\n\n        let (tx, rx) = mpsc::channel();\n        Self::spawn_thread(tx, connected, xinput_handle.clone());\n\n        // Coerce gamepads vector to slice\n        Ok(Gilrs { gamepads, rx })\n    }\n\n    pub(crate) fn next_event(&mut self) -> Option<Event> {\n        let ev = self.rx.try_recv().ok();\n        self.handle_evevnt(ev);\n\n        ev\n    }\n\n    pub(crate) fn next_event_blocking(&mut self, timeout: Option<Duration>) -> Option<Event> {\n        let ev = if let Some(tiemout) = timeout {\n            self.rx.recv_timeout(tiemout).ok()\n        } else {\n            self.rx.recv().ok()\n        };\n\n        self.handle_evevnt(ev);\n\n        ev\n    }\n\n    fn handle_evevnt(&mut self, ev: Option<Event>) {\n        if let Some(ev) = ev {\n            match ev.event {\n                EventType::Connected => self.gamepads[ev.id].is_connected = true,\n                EventType::Disconnected => self.gamepads[ev.id].is_connected = false,\n                _ => (),\n            }\n        }\n    }\n\n    pub fn gamepad(&self, id: usize) -> Option<&Gamepad> {\n        self.gamepads.get(id)\n    }\n\n    pub fn last_gamepad_hint(&self) -> usize {\n        self.gamepads.len()\n    }\n\n    fn spawn_thread(\n        tx: Sender<Event>,\n        connected: [bool; MAX_XINPUT_CONTROLLERS],\n        xinput_handle: Arc<XInputHandle>,\n    ) {\n        std::thread::Builder::new()\n            .name(\"gilrs\".to_owned())\n            .spawn(move || unsafe {\n                // Issue #70 fix - Maintain a prev_state per controller id. Otherwise the loop will compare the prev_state of a different controller.\n                let mut prev_states: [XState; MAX_XINPUT_CONTROLLERS] =\n                    [mem::zeroed::<XState>(); MAX_XINPUT_CONTROLLERS];\n                let mut connected = connected;\n                let mut counter = 0;\n\n                loop {\n                    for id in 0..MAX_XINPUT_CONTROLLERS {\n                        if *connected.get_unchecked(id)\n                            || counter % ITERATIONS_TO_CHECK_IF_CONNECTED == 0\n                        {\n                            match xinput_handle.get_state(id as u32) {\n                                Ok(XInputState { raw: state }) => {\n                                    if !connected[id] {\n                                        connected[id] = true;\n                                        let _ = tx.send(Event::new(id, EventType::Connected));\n                                    }\n\n                                    if state.dwPacketNumber != prev_states[id].dwPacketNumber {\n                                        Self::compare_state(\n                                            id,\n                                            &state.Gamepad,\n                                            &prev_states[id].Gamepad,\n                                            &tx,\n                                        );\n                                        prev_states[id] = state;\n                                    }\n                                }\n                                Err(XInputUsageError::DeviceNotConnected) if connected[id] => {\n                                    connected[id] = false;\n                                    let _ = tx.send(Event::new(id, EventType::Disconnected));\n                                }\n                                Err(XInputUsageError::DeviceNotConnected) => (),\n                                Err(e) => error!(\"Failed to get gamepad state: {:?}\", e),\n                            }\n                        }\n                    }\n\n                    counter = counter.wrapping_add(1);\n                    thread::sleep(Duration::from_millis(EVENT_THREAD_SLEEP_TIME));\n                }\n            })\n            .expect(\"failed to spawn thread\");\n    }\n\n    fn compare_state(id: usize, g: &XGamepad, pg: &XGamepad, tx: &Sender<Event>) {\n        if g.bLeftTrigger != pg.bLeftTrigger {\n            let _ = tx.send(Event::new(\n                id,\n                EventType::AxisValueChanged(\n                    g.bLeftTrigger as i32,\n                    crate::native_ev_codes::AXIS_LT2,\n                ),\n            ));\n        }\n        if g.bRightTrigger != pg.bRightTrigger {\n            let _ = tx.send(Event::new(\n                id,\n                EventType::AxisValueChanged(\n                    g.bRightTrigger as i32,\n                    crate::native_ev_codes::AXIS_RT2,\n                ),\n            ));\n        }\n        if g.sThumbLX != pg.sThumbLX {\n            let _ = tx.send(Event::new(\n                id,\n                EventType::AxisValueChanged(\n                    g.sThumbLX as i32,\n                    crate::native_ev_codes::AXIS_LSTICKX,\n                ),\n            ));\n        }\n        if g.sThumbLY != pg.sThumbLY {\n            let _ = tx.send(Event::new(\n                id,\n                EventType::AxisValueChanged(\n                    g.sThumbLY as i32,\n                    crate::native_ev_codes::AXIS_LSTICKY,\n                ),\n            ));\n        }\n        if g.sThumbRX != pg.sThumbRX {\n            let _ = tx.send(Event::new(\n                id,\n                EventType::AxisValueChanged(\n                    g.sThumbRX as i32,\n                    crate::native_ev_codes::AXIS_RSTICKX,\n                ),\n            ));\n        }\n        if g.sThumbRY != pg.sThumbRY {\n            let _ = tx.send(Event::new(\n                id,\n                EventType::AxisValueChanged(\n                    g.sThumbRY as i32,\n                    crate::native_ev_codes::AXIS_RSTICKY,\n                ),\n            ));\n        }\n        if !is_mask_eq(g.wButtons, pg.wButtons, XINPUT_GAMEPAD_DPAD_UP) {\n            let _ = match g.wButtons & XINPUT_GAMEPAD_DPAD_UP != 0 {\n                true => tx.send(Event::new(\n                    id,\n                    EventType::ButtonPressed(crate::native_ev_codes::BTN_DPAD_UP),\n                )),\n                false => tx.send(Event::new(\n                    id,\n                    EventType::ButtonReleased(crate::native_ev_codes::BTN_DPAD_UP),\n                )),\n            };\n        }\n        if !is_mask_eq(g.wButtons, pg.wButtons, XINPUT_GAMEPAD_DPAD_DOWN) {\n            let _ = match g.wButtons & XINPUT_GAMEPAD_DPAD_DOWN != 0 {\n                true => tx.send(Event::new(\n                    id,\n                    EventType::ButtonPressed(crate::native_ev_codes::BTN_DPAD_DOWN),\n                )),\n                false => tx.send(Event::new(\n                    id,\n                    EventType::ButtonReleased(crate::native_ev_codes::BTN_DPAD_DOWN),\n                )),\n            };\n        }\n        if !is_mask_eq(g.wButtons, pg.wButtons, XINPUT_GAMEPAD_DPAD_LEFT) {\n            let _ = match g.wButtons & XINPUT_GAMEPAD_DPAD_LEFT != 0 {\n                true => tx.send(Event::new(\n                    id,\n                    EventType::ButtonPressed(crate::native_ev_codes::BTN_DPAD_LEFT),\n                )),\n                false => tx.send(Event::new(\n                    id,\n                    EventType::ButtonReleased(crate::native_ev_codes::BTN_DPAD_LEFT),\n                )),\n            };\n        }\n        if !is_mask_eq(g.wButtons, pg.wButtons, XINPUT_GAMEPAD_DPAD_RIGHT) {\n            let _ = match g.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT != 0 {\n                true => tx.send(Event::new(\n                    id,\n                    EventType::ButtonPressed(crate::native_ev_codes::BTN_DPAD_RIGHT),\n                )),\n                false => tx.send(Event::new(\n                    id,\n                    EventType::ButtonReleased(crate::native_ev_codes::BTN_DPAD_RIGHT),\n                )),\n            };\n        }\n        if !is_mask_eq(g.wButtons, pg.wButtons, XINPUT_GAMEPAD_START) {\n            let _ = match g.wButtons & XINPUT_GAMEPAD_START != 0 {\n                true => tx.send(Event::new(\n                    id,\n                    EventType::ButtonPressed(crate::native_ev_codes::BTN_START),\n                )),\n                false => tx.send(Event::new(\n                    id,\n                    EventType::ButtonReleased(crate::native_ev_codes::BTN_START),\n                )),\n            };\n        }\n        if !is_mask_eq(g.wButtons, pg.wButtons, XINPUT_GAMEPAD_BACK) {\n            let _ = match g.wButtons & XINPUT_GAMEPAD_BACK != 0 {\n                true => tx.send(Event::new(\n                    id,\n                    EventType::ButtonPressed(crate::native_ev_codes::BTN_SELECT),\n                )),\n                false => tx.send(Event::new(\n                    id,\n                    EventType::ButtonReleased(crate::native_ev_codes::BTN_SELECT),\n                )),\n            };\n        }\n        if !is_mask_eq(g.wButtons, pg.wButtons, XINPUT_GAMEPAD_LEFT_THUMB) {\n            let _ = match g.wButtons & XINPUT_GAMEPAD_LEFT_THUMB != 0 {\n                true => tx.send(Event::new(\n                    id,\n                    EventType::ButtonPressed(crate::native_ev_codes::BTN_LTHUMB),\n                )),\n                false => tx.send(Event::new(\n                    id,\n                    EventType::ButtonReleased(crate::native_ev_codes::BTN_LTHUMB),\n                )),\n            };\n        }\n        if !is_mask_eq(g.wButtons, pg.wButtons, XINPUT_GAMEPAD_RIGHT_THUMB) {\n            let _ = match g.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB != 0 {\n                true => tx.send(Event::new(\n                    id,\n                    EventType::ButtonPressed(crate::native_ev_codes::BTN_RTHUMB),\n                )),\n                false => tx.send(Event::new(\n                    id,\n                    EventType::ButtonReleased(crate::native_ev_codes::BTN_RTHUMB),\n                )),\n            };\n        }\n        if !is_mask_eq(g.wButtons, pg.wButtons, XINPUT_GAMEPAD_LEFT_SHOULDER) {\n            let _ = match g.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER != 0 {\n                true => tx.send(Event::new(\n                    id,\n                    EventType::ButtonPressed(crate::native_ev_codes::BTN_LT),\n                )),\n                false => tx.send(Event::new(\n                    id,\n                    EventType::ButtonReleased(crate::native_ev_codes::BTN_LT),\n                )),\n            };\n        }\n        if !is_mask_eq(g.wButtons, pg.wButtons, XINPUT_GAMEPAD_RIGHT_SHOULDER) {\n            let _ = match g.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER != 0 {\n                true => tx.send(Event::new(\n                    id,\n                    EventType::ButtonPressed(crate::native_ev_codes::BTN_RT),\n                )),\n                false => tx.send(Event::new(\n                    id,\n                    EventType::ButtonReleased(crate::native_ev_codes::BTN_RT),\n                )),\n            };\n        }\n        if !is_mask_eq(g.wButtons, pg.wButtons, XINPUT_GAMEPAD_A) {\n            let _ = match g.wButtons & XINPUT_GAMEPAD_A != 0 {\n                true => tx.send(Event::new(\n                    id,\n                    EventType::ButtonPressed(crate::native_ev_codes::BTN_SOUTH),\n                )),\n                false => tx.send(Event::new(\n                    id,\n                    EventType::ButtonReleased(crate::native_ev_codes::BTN_SOUTH),\n                )),\n            };\n        }\n        if !is_mask_eq(g.wButtons, pg.wButtons, XINPUT_GAMEPAD_B) {\n            let _ = match g.wButtons & XINPUT_GAMEPAD_B != 0 {\n                true => tx.send(Event::new(\n                    id,\n                    EventType::ButtonPressed(crate::native_ev_codes::BTN_EAST),\n                )),\n                false => tx.send(Event::new(\n                    id,\n                    EventType::ButtonReleased(crate::native_ev_codes::BTN_EAST),\n                )),\n            };\n        }\n        if !is_mask_eq(g.wButtons, pg.wButtons, XINPUT_GAMEPAD_X) {\n            let _ = match g.wButtons & XINPUT_GAMEPAD_X != 0 {\n                true => tx.send(Event::new(\n                    id,\n                    EventType::ButtonPressed(crate::native_ev_codes::BTN_WEST),\n                )),\n                false => tx.send(Event::new(\n                    id,\n                    EventType::ButtonReleased(crate::native_ev_codes::BTN_WEST),\n                )),\n            };\n        }\n        if !is_mask_eq(g.wButtons, pg.wButtons, XINPUT_GAMEPAD_Y) {\n            let _ = match g.wButtons & XINPUT_GAMEPAD_Y != 0 {\n                true => tx.send(Event::new(\n                    id,\n                    EventType::ButtonPressed(crate::native_ev_codes::BTN_NORTH),\n                )),\n                false => tx.send(Event::new(\n                    id,\n                    EventType::ButtonReleased(crate::native_ev_codes::BTN_NORTH),\n                )),\n            };\n        }\n    }\n}\n\n#[derive(Debug)]\npub struct Gamepad {\n    uuid: Uuid,\n    id: u32,\n    is_connected: bool,\n    xinput_handle: Arc<XInputHandle>,\n}\n\nimpl Gamepad {\n    fn new(id: u32, xinput_handle: Arc<XInputHandle>) -> Gamepad {\n        let is_connected = xinput_handle.get_state(id).is_ok();\n\n        Gamepad {\n            uuid: Uuid::nil(),\n            id,\n            is_connected,\n            xinput_handle,\n        }\n    }\n\n    pub fn name(&self) -> &str {\n        \"Xbox Controller\"\n    }\n\n    pub fn uuid(&self) -> Uuid {\n        self.uuid\n    }\n\n    pub fn vendor_id(&self) -> Option<u16> {\n        None\n    }\n\n    pub fn product_id(&self) -> Option<u16> {\n        None\n    }\n\n    pub fn is_connected(&self) -> bool {\n        self.is_connected\n    }\n\n    pub fn power_info(&self) -> PowerInfo {\n        match self.xinput_handle.get_gamepad_battery_information(self.id) {\n            Ok(binfo) => match binfo.battery_type {\n                BatteryType::WIRED => PowerInfo::Wired,\n                BatteryType::ALKALINE | BatteryType::NIMH => {\n                    let lvl = match binfo.battery_level {\n                        BatteryLevel::EMPTY => 0,\n                        BatteryLevel::LOW => 33,\n                        BatteryLevel::MEDIUM => 67,\n                        BatteryLevel::FULL => 100,\n                        lvl => {\n                            trace!(\"Unexpected battery level: {}\", lvl.0);\n\n                            100\n                        }\n                    };\n                    if lvl == 100 {\n                        PowerInfo::Charged\n                    } else {\n                        PowerInfo::Discharging(lvl)\n                    }\n                }\n                _ => PowerInfo::Unknown,\n            },\n            Err(e) => {\n                debug!(\"Failed to get battery info: {:?}\", e);\n\n                PowerInfo::Unknown\n            }\n        }\n    }\n\n    pub fn is_ff_supported(&self) -> bool {\n        true\n    }\n\n    pub fn ff_device(&self) -> Option<FfDevice> {\n        Some(FfDevice::new(self.id, self.xinput_handle.clone()))\n    }\n\n    pub fn buttons(&self) -> &[EvCode] {\n        &native_ev_codes::BUTTONS\n    }\n\n    pub fn axes(&self) -> &[EvCode] {\n        &native_ev_codes::AXES\n    }\n\n    pub(crate) fn axis_info(&self, nec: EvCode) -> Option<&AxisInfo> {\n        native_ev_codes::AXES_INFO\n            .get(nec.0 as usize)\n            .and_then(|o| o.as_ref())\n    }\n}\n\n#[inline(always)]\nfn is_mask_eq(l: u16, r: u16, mask: u16) -> bool {\n    (l & mask != 0) == (r & mask != 0)\n}\n\n#[cfg(feature = \"serde-serialize\")]\nuse serde::{Deserialize, Serialize};\n\n#[cfg_attr(feature = \"serde-serialize\", derive(Serialize, Deserialize))]\n#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]\npub struct EvCode(u8);\n\nimpl EvCode {\n    pub fn into_u32(self) -> u32 {\n        self.0 as u32\n    }\n}\n\nimpl Display for EvCode {\n    fn fmt(&self, f: &mut Formatter) -> FmtResult {\n        self.0.fmt(f)\n    }\n}\n\n#[derive(Debug)]\nenum Error {\n    FailedToLoadDll(XInputLoadingFailure),\n}\n\nimpl StdError for Error {}\n\nimpl Display for Error {\n    fn fmt(&self, f: &mut Formatter) -> FmtResult {\n        match self {\n            Error::FailedToLoadDll(e) => {\n                f.write_fmt(format_args!(\"Failed to load XInput DLL {:?}\", e))\n            }\n        }\n    }\n}\n\npub mod native_ev_codes {\n    use winapi::um::xinput::{\n        XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE,\n        XINPUT_GAMEPAD_TRIGGER_THRESHOLD,\n    };\n\n    use super::EvCode;\n    use crate::AxisInfo;\n\n    pub const AXIS_LSTICKX: EvCode = EvCode(0);\n    pub const AXIS_LSTICKY: EvCode = EvCode(1);\n    pub const AXIS_LEFTZ: EvCode = EvCode(2);\n    pub const AXIS_RSTICKX: EvCode = EvCode(3);\n    pub const AXIS_RSTICKY: EvCode = EvCode(4);\n    pub const AXIS_RIGHTZ: EvCode = EvCode(5);\n    pub const AXIS_DPADX: EvCode = EvCode(6);\n    pub const AXIS_DPADY: EvCode = EvCode(7);\n    pub const AXIS_RT: EvCode = EvCode(8);\n    pub const AXIS_LT: EvCode = EvCode(9);\n    pub const AXIS_RT2: EvCode = EvCode(10);\n    pub const AXIS_LT2: EvCode = EvCode(11);\n\n    pub const BTN_SOUTH: EvCode = EvCode(12);\n    pub const BTN_EAST: EvCode = EvCode(13);\n    pub const BTN_C: EvCode = EvCode(14);\n    pub const BTN_NORTH: EvCode = EvCode(15);\n    pub const BTN_WEST: EvCode = EvCode(16);\n    pub const BTN_Z: EvCode = EvCode(17);\n    pub const BTN_LT: EvCode = EvCode(18);\n    pub const BTN_RT: EvCode = EvCode(19);\n    pub const BTN_LT2: EvCode = EvCode(20);\n    pub const BTN_RT2: EvCode = EvCode(21);\n    pub const BTN_SELECT: EvCode = EvCode(22);\n    pub const BTN_START: EvCode = EvCode(23);\n    pub const BTN_MODE: EvCode = EvCode(24);\n    pub const BTN_LTHUMB: EvCode = EvCode(25);\n    pub const BTN_RTHUMB: EvCode = EvCode(26);\n\n    pub const BTN_DPAD_UP: EvCode = EvCode(27);\n    pub const BTN_DPAD_DOWN: EvCode = EvCode(28);\n    pub const BTN_DPAD_LEFT: EvCode = EvCode(29);\n    pub const BTN_DPAD_RIGHT: EvCode = EvCode(30);\n\n    pub(super) static BUTTONS: [EvCode; 15] = [\n        BTN_SOUTH,\n        BTN_EAST,\n        BTN_NORTH,\n        BTN_WEST,\n        BTN_LT,\n        BTN_RT,\n        BTN_SELECT,\n        BTN_START,\n        BTN_MODE,\n        BTN_LTHUMB,\n        BTN_RTHUMB,\n        BTN_DPAD_UP,\n        BTN_DPAD_DOWN,\n        BTN_DPAD_LEFT,\n        BTN_DPAD_RIGHT,\n    ];\n\n    pub(super) static AXES: [EvCode; 6] = [\n        AXIS_LSTICKX,\n        AXIS_LSTICKY,\n        AXIS_RSTICKX,\n        AXIS_RSTICKY,\n        AXIS_RT2,\n        AXIS_LT2,\n    ];\n\n    pub(super) static AXES_INFO: [Option<AxisInfo>; 12] = [\n        // LeftStickX\n        Some(AxisInfo {\n            min: i16::MIN as i32,\n            max: i16::MAX as i32,\n            deadzone: Some(XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE as u32),\n        }),\n        // LeftStickY\n        Some(AxisInfo {\n            min: i16::MIN as i32,\n            max: i16::MAX as i32,\n            deadzone: Some(XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE as u32),\n        }),\n        // LeftZ\n        None,\n        // RightStickX\n        Some(AxisInfo {\n            min: i16::MIN as i32,\n            max: i16::MAX as i32,\n            deadzone: Some(XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE as u32),\n        }),\n        // RightStickY\n        Some(AxisInfo {\n            min: i16::MIN as i32,\n            max: i16::MAX as i32,\n            deadzone: Some(XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE as u32),\n        }),\n        // RightZ\n        None,\n        // DPadX\n        None,\n        // DPadY\n        None,\n        // RightTrigger\n        None,\n        // LeftTrigger\n        None,\n        // RightTrigger2\n        Some(AxisInfo {\n            min: u8::MIN as i32,\n            max: u8::MAX as i32,\n            deadzone: Some(XINPUT_GAMEPAD_TRIGGER_THRESHOLD as u32),\n        }),\n        // LeftTrigger2\n        Some(AxisInfo {\n            min: u8::MIN as i32,\n            max: u8::MAX as i32,\n            deadzone: Some(XINPUT_GAMEPAD_TRIGGER_THRESHOLD as u32),\n        }),\n    ];\n}\n"
  },
  {
    "path": "gilrs-core/src/platform/windows_xinput/mod.rs",
    "content": "// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers\n//\n// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or\n// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or\n// http://opensource.org/licenses/MIT>, at your option. This file may not be\n// copied, modified, or distributed except according to those terms.\nmod ff;\nmod gamepad;\n\npub use self::ff::Device as FfDevice;\npub use self::gamepad::{native_ev_codes, EvCode, Gamepad, Gilrs};\n\npub const IS_Y_AXIS_REVERSED: bool = false;\n"
  },
  {
    "path": "gilrs-core/src/utils.rs",
    "content": "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, array: &[u8]) -> bool {\n    (array[(n / 8) as usize] >> (n % 8)) & 1 != 0\n}\n\n#[cfg(not(target_arch = \"wasm32\"))]\npub fn time_now() -> SystemTime {\n    SystemTime::now()\n}\n\n#[cfg(target_arch = \"wasm32\")]\npub fn time_now() -> SystemTime {\n    use js_sys::Date;\n    use std::time::Duration;\n\n    let offset = Duration::from_millis(Date::now() as u64);\n    SystemTime::UNIX_EPOCH + offset\n}\n"
  },
  {
    "path": "rustfmt.toml",
    "content": "unstable_features = true\ncondense_wildcard_suffixes = true\nerror_on_line_overflow = false\nreorder_imports = true\nuse_try_shorthand = true\nformat_strings = true\n"
  }
]