update
21
LICENSE
@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2017-2020 Michaël "e7d" Ferrand
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
77
README.md
@ -1,42 +1,60 @@
|
|||||||
# gamepad-viewer
|
# gamepad-viewer
|
||||||
|
|
||||||
Displays live status about gaming devices connected to your computer.
|
Displays live status about gaming devices connected to your computer.
|
||||||
|
|
||||||
## Demo
|
## Demo
|
||||||
|
|
||||||
[gamepad.e7d.io](https://gamepad.e7d.io/)
|
[gamepad.e7d.io](https://gamepad.e7d.io/)
|
||||||
|
|
||||||
|
## Adding to OBS
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> For OBS to display real-time controller inputs when the window isn't focused, OBS needs to be started with the command line flag `--disable-features=EnableWindowsGamingInputDataFetcher`
|
||||||
|
|
||||||
|
1. Start a listening server for gamepad-viewer with `node app.js`
|
||||||
|
2. Add a new browser source and paste the url (e.g. `http://localhost:8080/?color=white&triggers=meter&type=dualsense`)
|
||||||
|
3. Uncheck the following options
|
||||||
|
- Shutdown source when not visible
|
||||||
|
4. Set **_Page permissions_** to **_Full access to OBS (Start/Stop streaming without warning, etc.)_**
|
||||||
|
5. Click **_Refresh cache of current page_**
|
||||||
|
6. Press a button on your gamepad
|
||||||
|
|
||||||
|
If done correctly, the controller should vibrate and you should see your gamepad's inputs being displayed in real-time!
|
||||||
|
|
||||||
## Shortcuts
|
## Shortcuts
|
||||||
- `+`: Zoom in
|
|
||||||
- `-`: Zoom out
|
- `+`: Zoom in
|
||||||
- `0`: Reset zoom to 100%
|
- `-`: Zoom out
|
||||||
- `5`: Adjust zoom automatically to window
|
- `0`: Reset zoom to 100%
|
||||||
- `B`: Change background style
|
- `5`: Adjust zoom automatically to window
|
||||||
- `C`: Change active gamepad color
|
- `B`: Change background style
|
||||||
- `D`: Toggle debug mode for active gamepad
|
- `C`: Change active gamepad color
|
||||||
- `H`: Toggle help menu
|
- `D`: Toggle debug mode for active gamepad
|
||||||
- `T`: Toggle triggers mode (opacity / meter)
|
- `H`: Toggle help menu
|
||||||
|
- `T`: Toggle triggers mode (opacity / meter)
|
||||||
- `Delete` or `Esc`: Clear active gamepad
|
- `Delete` or `Esc`: Clear active gamepad
|
||||||
|
|
||||||
## Supported gamepads
|
## Supported gamepads
|
||||||
|
|
||||||
- Sony DualSense*
|
- Sony DualSense\*
|
||||||
- Sony DualShock 4*
|
- Sony DualShock 4\*
|
||||||
- Microsoft Xbox Series Controller*
|
- Microsoft Xbox Series Controller\*
|
||||||
- Microsoft Xbox One Controller*
|
- Microsoft Xbox One Controller\*
|
||||||
- Microsoft Xbox 360 Controller
|
- Microsoft Xbox 360 Controller
|
||||||
- Nintendo Switch Pro Controller*
|
- Nintendo Switch Pro Controller\*
|
||||||
- Xinput compatible gamepads
|
- Xinput compatible gamepads
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
All the parameters are set in the URL.
|
All the parameters are set in the URL.
|
||||||
| **Parameter** | **Description** | **Value** | **Default** |
|
| **Parameter** | **Description** | **Value** | **Default** |
|
||||||
|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|
|
|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|
|
||||||
| gamepad | The controller ID of a specific gamepad to use. Please find the controller ID by using the help displayed using the `h` keyboard button. | i.e.: `Microsoft Controller (STANDARD GAMEPAD Vendor: 045e Product: 0b00)` | |
|
| gamepad | The controller ID of a specific gamepad to use. Please find the controller ID by using the help displayed using the `h` keyboard button. | i.e.: `Microsoft Controller (STANDARD GAMEPAD Vendor: 045e Product: 0b00)` | |
|
||||||
| type | The type of controller (the "skin") to display to the screen.<br>When possible, the type of controller is auto detected to display the corresponding skin. | - `debug`<br>- `dualsense`<br>- `ds4`<br>- `xbox-one`<br>- `telemetry` (beta mode for wheels that requires specific configuration) | |
|
| type | The type of controller (the "skin") to display to the screen.<br>When possible, the type of controller is auto detected to display the corresponding skin. | - `debug`<br>- `dualsense`<br>- `ds4`<br>- `xbox-one`<br>- `telemetry` (beta mode for wheels that requires specific configuration) | |
|
||||||
| background | The background color. | - `transparent`<br>- `checkered`<br>- `grey`<br>- `black`<br>- `white`<br>- `lime`<br>- `magenta` | `transparent` |
|
| background | The background color. | - `transparent`<br>- `checkered`<br>- `grey`<br>- `black`<br>- `white`<br>- `lime`<br>- `magenta` | `transparent` |
|
||||||
| color | The displayed gamepad color. | Depending of the selected display `type`. | |
|
| color | The displayed gamepad color. | Depending of the selected display `type`. | |
|
||||||
| triggers | The triggers display mode, when applicable. | - `opacity`<br>- `meter` | `opacity` |
|
| triggers | The triggers display mode, when applicable. | - `opacity`<br>- `meter` | `opacity` |
|
||||||
| zoom | The zoom level of the display controller. | Any integer value from `0.1` to `4`, corresponding to a zoom value for 10% to 400%.<br>When no value is set, the zoom is auto adjusted to fit the window size. | |
|
| zoom | The zoom level of the display controller. | Any integer value from `0.1` to `4`, corresponding to a zoom value for 10% to 400%.<br>When no value is set, the zoom is auto adjusted to fit the window size. | |
|
||||||
|
|
||||||
## Telemetry configuration
|
## Telemetry configuration
|
||||||
|
|
||||||
@ -49,18 +67,21 @@ The `telemetry` mode requires for you to determine which axes or buttons will be
|
|||||||
To help you do so, you need to access the Gamepad Viewer with the `debug` skin. To do so, you can go to https://gamepad.e7d.io/?type=debug, and press any button for detection.
|
To help you do so, you need to access the Gamepad Viewer with the `debug` skin. To do so, you can go to https://gamepad.e7d.io/?type=debug, and press any button for detection.
|
||||||
|
|
||||||
For each of the axes or buttons you want to use, activate them one by one, and proceed as following:
|
For each of the axes or buttons you want to use, activate them one by one, and proceed as following:
|
||||||
|
|
||||||
- observe the changing value in the `Axes` and `Buttons` sections to determine the correct entry
|
- observe the changing value in the `Axes` and `Buttons` sections to determine the correct entry
|
||||||
- note down its type (axis or button) and its index.
|
- note down its type (axis or button) and its index.
|
||||||
- for the clutch, brake and throttle, note down the minimum (when released) and maximum (when pressed) values
|
- for the clutch, brake and throttle, note down the minimum (when released) and maximum (when pressed) values
|
||||||
- for the direction, note down the minimum (left) and maximum (right) values
|
- for the direction, note down the minimum (left) and maximum (right) values
|
||||||
|
|
||||||
For example with a **Thrustmaster T818**, you should get the following values:
|
For example with a **Thrustmaster T818**, you should get the following values:
|
||||||
|
|
||||||
- clutch: axis 6, min 1, max -1
|
- clutch: axis 6, min 1, max -1
|
||||||
- brake: axis 1, min 1, max -1
|
- brake: axis 1, min 1, max -1
|
||||||
- throttle: axis 5, min 1, max -1
|
- throttle: axis 5, min 1, max -1
|
||||||
- direction: axis 0, min -1, max 1
|
- direction: axis 0, min -1, max 1
|
||||||
|
|
||||||
Another example with a **Xbox one controller** with the A button used as clutch, the triggers as brake and throttle, and the left stick as direction. You should get the following values:
|
Another example with a **Xbox one controller** with the A button used as clutch, the triggers as brake and throttle, and the left stick as direction. You should get the following values:
|
||||||
|
|
||||||
- clutch: button 0, min 0, max 1
|
- clutch: button 0, min 0, max 1
|
||||||
- brake: button 6, min 0, max 1
|
- brake: button 6, min 0, max 1
|
||||||
- throttle: button 7, min 0, max 1
|
- throttle: button 7, min 0, max 1
|
||||||
@ -72,19 +93,19 @@ All the parameters are set in the URL.
|
|||||||
| **Parameter** | **Description** | **Value** | **Default** |
|
| **Parameter** | **Description** | **Value** | **Default** |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| clutchType | The clutch type, either an axis or a button. | `axis` or `button` | `axis` |
|
| clutchType | The clutch type, either an axis or a button. | `axis` or `button` | `axis` |
|
||||||
| clutchIndex | The index of the clutch axis or button. | A positive integer. | |
|
| clutchIndex | The index of the clutch axis or button. | A positive integer. | |
|
||||||
| clutchMin | The minimum value when the clutch is fully released. | `-1`, `0` or `1` | `-1` |
|
| clutchMin | The minimum value when the clutch is fully released. | `-1`, `0` or `1` | `-1` |
|
||||||
| clutchMax | The maximum value when the clutch is fully pressed. | `-1`, `0` or `1` | `1` |
|
| clutchMax | The maximum value when the clutch is fully pressed. | `-1`, `0` or `1` | `1` |
|
||||||
| brakeType | The brake type, either an axis or a button. | `axis` or `button` | `axis` |
|
| brakeType | The brake type, either an axis or a button. | `axis` or `button` | `axis` |
|
||||||
| brakeIndex | The index of the brake axis or button. | A positive integer. | |
|
| brakeIndex | The index of the brake axis or button. | A positive integer. | |
|
||||||
| brakeMin | The minimum value when the brake is fully released. | `-1`, `0` or `1` | `-1` |
|
| brakeMin | The minimum value when the brake is fully released. | `-1`, `0` or `1` | `-1` |
|
||||||
| brakeMax | The maximum value when the brake is fully pressed. | `-1`, `0` or `1` | `1` |
|
| brakeMax | The maximum value when the brake is fully pressed. | `-1`, `0` or `1` | `1` |
|
||||||
| throttleType | The throttle type, either an axis or a button. | `axis` or `button` | `axis` |
|
| throttleType | The throttle type, either an axis or a button. | `axis` or `button` | `axis` |
|
||||||
| throttleIndex | The index of the throttle axis or button. | A positive integer. | |
|
| throttleIndex | The index of the throttle axis or button. | A positive integer. | |
|
||||||
| throttleMin | The minimum value when the throttle is fully released. | `-1`, `0` or `1` | `-1` |
|
| throttleMin | The minimum value when the throttle is fully released. | `-1`, `0` or `1` | `-1` |
|
||||||
| throttleMax | The maximum value when the throttle is fully pressed. | `-1`, `0` or `1` | `1` |
|
| throttleMax | The maximum value when the throttle is fully pressed. | `-1`, `0` or `1` | `1` |
|
||||||
| directionType | The direction type, either an axis or a button. | `axis` or `button` | `axis` |
|
| directionType | The direction type, either an axis or a button. | `axis` or `button` | `axis` |
|
||||||
| directionIndex | The index of the direction axis or button. | A positive integer. | |
|
| directionIndex | The index of the direction axis or button. | A positive integer. | |
|
||||||
| directionMin | The value when the direction is fully on the left. | `-1`, `0` or `1` | `-1` |
|
| directionMin | The value when the direction is fully on the left. | `-1`, `0` or `1` | `-1` |
|
||||||
| directionMax | The value when the direction is fully on the right. | `-1`, `0` or `1` | `1` |
|
| directionMax | The value when the direction is fully on the right. | `-1`, `0` or `1` | `1` |
|
||||||
| directionDegrees | The angle in degrees to represent the direction changes.<br>Common wheel values are `360`, `900` and `1080`. | A positive integer. | `360` |
|
| directionDegrees | The angle in degrees to represent the direction changes.<br>Common wheel values are `360`, `900` and `1080`. | A positive integer. | `360` |
|
||||||
@ -94,12 +115,15 @@ All the parameters are set in the URL.
|
|||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
Some working examples:
|
Some working examples:
|
||||||
|
|
||||||
- Thrustmaster T818, with 900 degrees direction: http://gamepad.e7d.io/?gamepad=044f-b696&type=telemetry&clutchIndex=6&clutchMin=1&clutchMax=-1&brakeIndex=1&brakeMin=1&brakeMax=-1&throttleIndex=5&throttleMin=1&throttleMax=-1&directionIndex=0&directionDegrees=900
|
- Thrustmaster T818, with 900 degrees direction: http://gamepad.e7d.io/?gamepad=044f-b696&type=telemetry&clutchIndex=6&clutchMin=1&clutchMax=-1&brakeIndex=1&brakeMin=1&brakeMax=-1&throttleIndex=5&throttleMin=1&throttleMax=-1&directionIndex=0&directionDegrees=900
|
||||||
- DualShock 4 or Xbox Controller, with 180 degrees direction representation: http://gamepad.e7d.io/?gamepad=045e-0b00&type=telemetry&brakeType=button&brakeIndex=6&brakeMin=0&brakeMax=1&throttleType=button&throttleIndex=7&throttleMin=0&throttleMax=1&directionIndex=0&directionDegrees=180
|
- DualShock 4 or Xbox Controller, with 180 degrees direction representation: http://gamepad.e7d.io/?gamepad=045e-0b00&type=telemetry&brakeType=button&brakeIndex=6&brakeMin=0&brakeMax=1&throttleType=button&throttleIndex=7&throttleMin=0&throttleMax=1&directionIndex=0&directionDegrees=180
|
||||||
- DualShock 4 or Xbox Controller, with clutch as A or Cross button, and 180 degrees direction representation: http://gamepad.e7d.io/?gamepad=045e-0b00&type=telemetry&clutchType=button&clutchIndex=0&clutchMin=0&clutchMax=1&brakeType=button&brakeIndex=6&brakeMin=0&brakeMax=1&throttleType=button&throttleIndex=7&throttleMin=0&throttleMax=1&directionIndex=0&directionDegrees=180
|
- DualShock 4 or Xbox Controller, with clutch as A or Cross button, and 180 degrees direction representation: http://gamepad.e7d.io/?gamepad=045e-0b00&type=telemetry&clutchType=button&clutchIndex=0&clutchMin=0&clutchMax=1&brakeType=button&brakeIndex=6&brakeMin=0&brakeMax=1&throttleType=button&throttleIndex=7&throttleMin=0&throttleMax=1&directionIndex=0&directionDegrees=180
|
||||||
|
|
||||||
## How to use with OBS Studio
|
## How to use with OBS Studio
|
||||||
|
|
||||||
Please read below:
|
Please read below:
|
||||||
|
|
||||||
- Open [gamepad.e7d.io](https://gamepad.e7d.io/) in your browser
|
- Open [gamepad.e7d.io](https://gamepad.e7d.io/) in your browser
|
||||||
- Activate the controller you want to display in OBS by long pressing one of its buttons
|
- Activate the controller you want to display in OBS by long pressing one of its buttons
|
||||||
- Configure it as desired: change skin, controller color, gamepad color, zoom...
|
- Configure it as desired: change skin, controller color, gamepad color, zoom...
|
||||||
@ -111,8 +135,9 @@ Please read below:
|
|||||||
- Press OK
|
- Press OK
|
||||||
- Adjust position and size of the source as you will
|
- Adjust position and size of the source as you will
|
||||||
|
|
||||||
*: These gamepads work both wired and wireless via Bluetooth
|
\*: These gamepads work both wired and wireless via Bluetooth
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
DualShock 4 and Xbox One skins from [gamepadviewer.com](https://gamepadviewer.com/)
|
DualShock 4 and Xbox One skins from [gamepadviewer.com](https://gamepadviewer.com/)
|
||||||
DualSense skin from [justEhCupcake](https://github.com/justEhCupcake/justEhCupcake.github.io/tree/main/PS5_Display_Pics)
|
DualSense skin from [justEhCupcake](https://github.com/justEhCupcake/justEhCupcake.github.io/tree/main/PS5_Display_Pics)
|
||||||
|
43
app.js
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// app.js
|
||||||
|
const http = require("http");
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
const url = require("url");
|
||||||
|
|
||||||
|
const port = 8080;
|
||||||
|
const host = "0.0.0.0";
|
||||||
|
const mimeTypes = {
|
||||||
|
".html": "text/html",
|
||||||
|
".css": "text/css",
|
||||||
|
".js": "application/javascript",
|
||||||
|
".svg": "image/svg+xml",
|
||||||
|
".png": "image/png",
|
||||||
|
".jpg": "image/jpeg",
|
||||||
|
".jpeg": "image/jpeg",
|
||||||
|
".gif": "image/gif",
|
||||||
|
".ico": "image/x-icon",
|
||||||
|
".json": "application/json",
|
||||||
|
".txt": "text/plain",
|
||||||
|
};
|
||||||
|
|
||||||
|
http.createServer((req, res) => {
|
||||||
|
// Parse the URL and extract just the pathname, ignoring query parameters
|
||||||
|
const parsedUrl = url.parse(req.url);
|
||||||
|
let filePath = "." + decodeURIComponent(parsedUrl.pathname);
|
||||||
|
if (filePath === "./") filePath = "./index.html";
|
||||||
|
|
||||||
|
const ext = path.extname(filePath).toLowerCase();
|
||||||
|
const contentType = mimeTypes[ext] || "application/octet-stream";
|
||||||
|
|
||||||
|
fs.readFile(filePath, (err, content) => {
|
||||||
|
if (err) {
|
||||||
|
res.writeHead(404);
|
||||||
|
res.end("404 Not Found");
|
||||||
|
} else {
|
||||||
|
res.writeHead(200, { "Content-Type": contentType });
|
||||||
|
res.end(content);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).listen(port, host, () => {
|
||||||
|
console.log(`Server running at http://${host}:${port}/`);
|
||||||
|
});
|
@ -2,7 +2,7 @@
|
|||||||
<browserconfig>
|
<browserconfig>
|
||||||
<msapplication>
|
<msapplication>
|
||||||
<tile>
|
<tile>
|
||||||
<square150x150logo src="/mstile-150x150.png"/>
|
<square150x150logo src="img/mstile-150x150.png"/>
|
||||||
<TileColor>#666666</TileColor>
|
<TileColor>#666666</TileColor>
|
||||||
</tile>
|
</tile>
|
||||||
</msapplication>
|
</msapplication>
|
||||||
|
@ -141,7 +141,7 @@ body.unsupported #gamepad {
|
|||||||
align-self: center;
|
align-self: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
#overlay>span {
|
#overlay > span {
|
||||||
padding: 0 8px;
|
padding: 0 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,7 +228,7 @@ body.unsupported #gamepad {
|
|||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popout table>thead>tr>th {
|
.popout table > thead > tr > th {
|
||||||
border-bottom: 2px solid #ddd;
|
border-bottom: 2px solid #ddd;
|
||||||
border-top: 0;
|
border-top: 0;
|
||||||
vertical-align: bottom;
|
vertical-align: bottom;
|
||||||
@ -261,21 +261,29 @@ kbd[title],
|
|||||||
}
|
}
|
||||||
|
|
||||||
kbd {
|
kbd {
|
||||||
-moz-box-shadow: inset 0 0 1px rgb(150, 150, 150),
|
-moz-box-shadow:
|
||||||
inset 0 -0.05em 0.4em rgb(80, 80, 80), 0 0.1em 0 rgb(30, 30, 30),
|
inset 0 0 1px rgb(150, 150, 150),
|
||||||
|
inset 0 -0.05em 0.4em rgb(80, 80, 80),
|
||||||
|
0 0.1em 0 rgb(30, 30, 30),
|
||||||
0 0.1em 0.1em rgba(0, 0, 0, 0.3);
|
0 0.1em 0.1em rgba(0, 0, 0, 0.3);
|
||||||
-webkit-box-shadow: inset 0 0 1px rgb(150, 150, 150),
|
-webkit-box-shadow:
|
||||||
inset 0 -0.05em 0.4em rgb(80, 80, 80), 0 0.1em 0 rgb(30, 30, 30),
|
inset 0 0 1px rgb(150, 150, 150),
|
||||||
|
inset 0 -0.05em 0.4em rgb(80, 80, 80),
|
||||||
|
0 0.1em 0 rgb(30, 30, 30),
|
||||||
0 0.1em 0.1em rgba(0, 0, 0, 0.3);
|
0 0.1em 0.1em rgba(0, 0, 0, 0.3);
|
||||||
background: -moz-linear-gradient(top, rgb(60, 60, 60), rgb(80, 80, 80));
|
background: -moz-linear-gradient(top, rgb(60, 60, 60), rgb(80, 80, 80));
|
||||||
background: -webkit-gradient(linear,
|
background: -webkit-gradient(
|
||||||
left top,
|
linear,
|
||||||
left bottom,
|
left top,
|
||||||
from(rgb(60, 60, 60)),
|
left bottom,
|
||||||
to(rgb(80, 80, 80)));
|
from(rgb(60, 60, 60)),
|
||||||
|
to(rgb(80, 80, 80))
|
||||||
|
);
|
||||||
background: rgb(80, 80, 80);
|
background: rgb(80, 80, 80);
|
||||||
box-shadow: inset 0 0 1px rgb(150, 150, 150),
|
box-shadow:
|
||||||
inset 0 -0.05em 0.4em rgb(80, 80, 80), 0 0.1em 0 rgb(30, 30, 30),
|
inset 0 0 1px rgb(150, 150, 150),
|
||||||
|
inset 0 -0.05em 0.4em rgb(80, 80, 80),
|
||||||
|
0 0.1em 0 rgb(30, 30, 30),
|
||||||
0 0.1em 0.1em rgba(0, 0, 0, 0.3);
|
0 0.1em 0.1em rgba(0, 0, 0, 0.3);
|
||||||
color: rgb(250, 250, 250);
|
color: rgb(250, 250, 250);
|
||||||
text-shadow: -1px -1px 0 rgb(70, 70, 70);
|
text-shadow: -1px -1px 0 rgb(70, 70, 70);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
services:
|
services:
|
||||||
web:
|
web:
|
||||||
image: nginx
|
image: nginx
|
||||||
ports:
|
ports:
|
||||||
- 8081:80
|
- 8081:80
|
||||||
volumes:
|
volumes:
|
||||||
- .:/usr/share/nginx/html:ro
|
- .:/usr/share/nginx/html:ro
|
||||||
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 8.2 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 517 B After Width: | Height: | Size: 517 B |
Before Width: | Height: | Size: 780 B After Width: | Height: | Size: 780 B |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
488
index.html
@ -1,227 +1,281 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<title>Gamepad Viewer</title>
|
||||||
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="Displays live status about gaming devices connected to your computer."
|
||||||
|
/>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<link
|
||||||
|
rel="apple-touch-icon"
|
||||||
|
sizes="180x180"
|
||||||
|
href="img/apple-touch-icon.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="32x32"
|
||||||
|
href="img/favicon-32x32.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="16x16"
|
||||||
|
href="img/favicon-16x16.png"
|
||||||
|
/>
|
||||||
|
<link rel="manifest" href="site.webmanifest" />
|
||||||
|
<link rel="mask-icon" href="img/safari-pinned-tab.svg" color="#333333" />
|
||||||
|
<meta name="apple-mobile-web-app-title" content="Gamepad Viewer" />
|
||||||
|
<meta name="application-name" content="Gamepad Viewer" />
|
||||||
|
<meta name="msapplication-TileColor" content="#666666" />
|
||||||
|
<meta name="theme-color" content="#ffffff" />
|
||||||
|
|
||||||
<head>
|
<link rel="stylesheet" href="css/gamepad.css" />
|
||||||
<meta charset="UTF-8">
|
</head>
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
||||||
<title>Gamepad Viewer</title>
|
|
||||||
<meta name="description" content="Displays live status about gaming devices connected to your computer.">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="apple-touch-icon.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="favicon-32x32.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="favicon-16x16.png">
|
|
||||||
<link rel="manifest" href="site.webmanifest">
|
|
||||||
<link rel="mask-icon" href="safari-pinned-tab.svg" color="#333333">
|
|
||||||
<meta name="apple-mobile-web-app-title" content="Gamepad Viewer">
|
|
||||||
<meta name="application-name" content="Gamepad Viewer">
|
|
||||||
<meta name="msapplication-TileColor" content="#666666">
|
|
||||||
<meta name="theme-color" content="#ffffff">
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="css/gamepad.css">
|
<body>
|
||||||
</head>
|
<div id="unsupported">
|
||||||
|
Sorry, but your browser does not support the
|
||||||
<body>
|
<a
|
||||||
<div id="unsupported">Sorry, but your browser does not support the <a target="_blank"
|
target="_blank"
|
||||||
href="https://developer.mozilla.org/docs/Web/API/Gamepad_API">Gamepad API</a>.</div>
|
href="https://developer.mozilla.org/docs/Web/API/Gamepad_API"
|
||||||
<div id="placeholder">
|
>Gamepad API</a
|
||||||
<svg version="1.1" viewBox="0 0 549.3125 367.98749" xml:space="preserve" xmlns="http://www.w3.org/2000/svg">
|
>.
|
||||||
<defs>
|
</div>
|
||||||
<clipPath id="a">
|
<div id="placeholder">
|
||||||
<path d="m0 294.39h439.45v-294.39h-439.45v294.39z"></path>
|
<svg
|
||||||
</clipPath>
|
version="1.1"
|
||||||
</defs>
|
viewBox="0 0 549.3125 367.98749"
|
||||||
<g transform="matrix(1.25 0 0 -1.25 0 367.99)">
|
xml:space="preserve"
|
||||||
<g clip-path="url(#a)">
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
<g transform="translate(291.61 74.1)">
|
>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="a">
|
||||||
<path
|
<path
|
||||||
d="m0 0c12.566-4e-3 24.723-4.474 34.3-12.61 0.226-0.192 0.443-0.396 0.649-0.61l49.151-51.259c0.15-0.151 0.299-0.301 0.46-0.451 15.29-14 39.54-11.75 51.669 5.11 30.55 42.46-4.5 145.09-43.609 235.94-0.845 1.916-2.417 3.417-4.37 4.17l-2.521 1c-2.458 0.96-4.272 3.089-4.83 5.67-1.569 7.229-6.358 13.343-13 16.6-40.389 19.86-65.7 21.27-79.109 8.13-1.479-1.499-3.485-2.36-5.591-2.4h-110.17c-2.127 0.035-4.154 0.908-5.64 2.43-13.4 13.14-38.71 11.73-79.1-8.13-6.642-3.257-11.431-9.371-13-16.6-0.542-2.585-2.345-4.726-4.8-5.7l-2.52-1c-1.963-0.765-3.537-2.285-4.37-4.22-39.1-90.78-74.15-193.43-43.6-235.89 12.13-16.89 36.39-19.09 51.669-5.11 0.161 0.14 0.31 0.29 0.461 0.451l49.15 51.259c0.206 0.214 0.423 0.418 0.65 0.61 9.576 8.136 21.733 12.606 34.299 12.61h143.77z"
|
d="m0 294.39h439.45v-294.39h-439.45v294.39z"
|
||||||
fill="#1f1f21"></path>
|
></path>
|
||||||
</g>
|
</clipPath>
|
||||||
<g transform="translate(278.88 161.81)">
|
</defs>
|
||||||
<path
|
<g transform="matrix(1.25 0 0 -1.25 0 367.99)">
|
||||||
d="m0 0c13.481 0 24.41-10.929 24.41-24.41 0-13.482-10.929-24.41-24.41-24.41s-24.41 10.928-24.41 24.41c0 13.481 10.929 24.41 24.41 24.41"
|
<g clip-path="url(#a)">
|
||||||
fill="#383838"></path>
|
<g transform="translate(291.61 74.1)">
|
||||||
</g>
|
<path
|
||||||
<g transform="translate(278.88 112.99)">
|
d="m0 0c12.566-4e-3 24.723-4.474 34.3-12.61 0.226-0.192 0.443-0.396 0.649-0.61l49.151-51.259c0.15-0.151 0.299-0.301 0.46-0.451 15.29-14 39.54-11.75 51.669 5.11 30.55 42.46-4.5 145.09-43.609 235.94-0.845 1.916-2.417 3.417-4.37 4.17l-2.521 1c-2.458 0.96-4.272 3.089-4.83 5.67-1.569 7.229-6.358 13.343-13 16.6-40.389 19.86-65.7 21.27-79.109 8.13-1.479-1.499-3.485-2.36-5.591-2.4h-110.17c-2.127 0.035-4.154 0.908-5.64 2.43-13.4 13.14-38.71 11.73-79.1-8.13-6.642-3.257-11.431-9.371-13-16.6-0.542-2.585-2.345-4.726-4.8-5.7l-2.52-1c-1.963-0.765-3.537-2.285-4.37-4.22-39.1-90.78-74.15-193.43-43.6-235.89 12.13-16.89 36.39-19.09 51.669-5.11 0.161 0.14 0.31 0.29 0.461 0.451l49.15 51.259c0.206 0.214 0.423 0.418 0.65 0.61 9.576 8.136 21.733 12.606 34.299 12.61h143.77z"
|
||||||
<path
|
fill="#1f1f21"
|
||||||
d="m0 0c-13.481 0-24.41 10.929-24.41 24.41 0 13.482 10.929 24.41 24.41 24.41s24.41-10.928 24.41-24.41v-0.01c-6e-3 -13.477-10.932-24.4-24.41-24.4m0 51.92c-15.193 0-27.51-12.316-27.51-27.51 0-15.193 12.317-27.51 27.51-27.51 15.189 0 27.505 12.311 27.51 27.5 6e-3 15.194-12.307 27.515-27.5 27.52h-0.01z"
|
></path>
|
||||||
fill="#191919"></path>
|
</g>
|
||||||
</g>
|
<g transform="translate(278.88 161.81)">
|
||||||
<g transform="translate(188.51 145.71)">
|
<path
|
||||||
<path
|
d="m0 0c13.481 0 24.41-10.929 24.41-24.41 0-13.482-10.929-24.41-24.41-24.41s-24.41 10.928-24.41 24.41c0 13.481 10.929 24.41 24.41 24.41"
|
||||||
d="m0 0h-11.88v11.87c0 3.314-2.686 6-6 6h-8.79c-3.313 0-6-2.686-6-6v-11.87h-11.84c-3.313 0-6-2.687-6-6v-8.79c0-3.314 2.687-6 6-6h11.87v-11.87c0-3.314 2.686-6 6-6h8.79c3.314 0 6 2.686 6 6v11.899h11.87c3.314 0 6 2.687 6 6v8.79c-0.016 3.311-2.71 5.982-6.02 5.971"
|
fill="#383838"
|
||||||
fill="#898989"></path>
|
></path>
|
||||||
</g>
|
</g>
|
||||||
<g transform="translate(111.34 230.94)">
|
<g transform="translate(278.88 112.99)">
|
||||||
<path
|
<path
|
||||||
d="m0 0c14.735 0 26.68-11.945 26.68-26.68s-11.945-26.68-26.68-26.68-26.68 11.945-26.68 26.68 11.945 26.68 26.68 26.68"
|
d="m0 0c-13.481 0-24.41 10.929-24.41 24.41 0 13.482 10.929 24.41 24.41 24.41s24.41-10.928 24.41-24.41v-0.01c-6e-3 -13.477-10.932-24.4-24.41-24.4m0 51.92c-15.193 0-27.51-12.316-27.51-27.51 0-15.193 12.317-27.51 27.51-27.51 15.189 0 27.505 12.311 27.51 27.5 6e-3 15.194-12.307 27.515-27.5 27.52h-0.01z"
|
||||||
fill="#191919"></path>
|
fill="#191919"
|
||||||
</g>
|
></path>
|
||||||
<g transform="translate(111.34 228.31)">
|
</g>
|
||||||
<path
|
<g transform="translate(188.51 145.71)">
|
||||||
d="m0 0c13.283 0 24.05-10.768 24.05-24.05 0-13.283-10.767-24.05-24.05-24.05-13.282 0-24.05 10.767-24.05 24.05 0 13.282 10.768 24.05 24.05 24.05"
|
<path
|
||||||
fill="#383838"></path>
|
d="m0 0h-11.88v11.87c0 3.314-2.686 6-6 6h-8.79c-3.313 0-6-2.686-6-6v-11.87h-11.84c-3.313 0-6-2.687-6-6v-8.79c0-3.314 2.687-6 6-6h11.87v-11.87c0-3.314 2.686-6 6-6h8.79c3.314 0 6 2.686 6 6v11.899h11.87c3.314 0 6 2.687 6 6v8.79c-0.016 3.311-2.71 5.982-6.02 5.971"
|
||||||
</g>
|
fill="#898989"
|
||||||
<g transform="translate(306.39 219)">
|
></path>
|
||||||
<path
|
</g>
|
||||||
d="m0 0c8.141 0 14.74-6.599 14.74-14.74 0-8.14-6.599-14.74-14.74-14.74s-14.739 6.6-14.739 14.74c0 8.141 6.598 14.74 14.739 14.74"
|
<g transform="translate(111.34 230.94)">
|
||||||
fill="#898989"></path>
|
<path
|
||||||
</g>
|
d="m0 0c14.735 0 26.68-11.945 26.68-26.68s-11.945-26.68-26.68-26.68-26.68 11.945-26.68 26.68 11.945 26.68 26.68 26.68"
|
||||||
<g id="a-button" transform="translate(335.87 189.37)">
|
fill="#191919"
|
||||||
<path
|
></path>
|
||||||
d="m0 0c8.141 0 14.74-6.599 14.74-14.74s-6.599-14.74-14.74-14.74-14.74 6.599-14.74 14.74 6.599 14.74 14.74 14.74"
|
</g>
|
||||||
fill="#898989"></path>
|
<g transform="translate(111.34 228.31)">
|
||||||
</g>
|
<path
|
||||||
<g transform="translate(335.87 248.63)">
|
d="m0 0c13.283 0 24.05-10.768 24.05-24.05 0-13.283-10.767-24.05-24.05-24.05-13.282 0-24.05 10.767-24.05 24.05 0 13.282 10.768 24.05 24.05 24.05"
|
||||||
<path
|
fill="#383838"
|
||||||
d="m0 0c8.141 0 14.74-6.6 14.74-14.74 0-8.141-6.599-14.74-14.74-14.74s-14.74 6.599-14.74 14.74c0 8.14 6.599 14.74 14.74 14.74"
|
></path>
|
||||||
fill="#898989"></path>
|
</g>
|
||||||
</g>
|
<g transform="translate(306.39 219)">
|
||||||
<g transform="translate(365.35 219)">
|
<path
|
||||||
<path
|
d="m0 0c8.141 0 14.74-6.599 14.74-14.74 0-8.14-6.599-14.74-14.74-14.74s-14.739 6.6-14.739 14.74c0 8.141 6.598 14.74 14.739 14.74"
|
||||||
d="m0 0c8.141 0 14.74-6.599 14.74-14.74 0-8.14-6.599-14.74-14.74-14.74s-14.739 6.6-14.739 14.74c0 8.141 6.598 14.74 14.739 14.74"
|
fill="#898989"
|
||||||
fill="#898989"></path>
|
></path>
|
||||||
|
</g>
|
||||||
|
<g id="a-button" transform="translate(335.87 189.37)">
|
||||||
|
<path
|
||||||
|
d="m0 0c8.141 0 14.74-6.599 14.74-14.74s-6.599-14.74-14.74-14.74-14.74 6.599-14.74 14.74 6.599 14.74 14.74 14.74"
|
||||||
|
fill="#898989"
|
||||||
|
></path>
|
||||||
|
</g>
|
||||||
|
<g transform="translate(335.87 248.63)">
|
||||||
|
<path
|
||||||
|
d="m0 0c8.141 0 14.74-6.6 14.74-14.74 0-8.141-6.599-14.74-14.74-14.74s-14.74 6.599-14.74 14.74c0 8.14 6.599 14.74 14.74 14.74"
|
||||||
|
fill="#898989"
|
||||||
|
></path>
|
||||||
|
</g>
|
||||||
|
<g transform="translate(365.35 219)">
|
||||||
|
<path
|
||||||
|
d="m0 0c8.141 0 14.74-6.599 14.74-14.74 0-8.14-6.599-14.74-14.74-14.74s-14.739 6.6-14.739 14.74c0 8.141 6.598 14.74 14.739 14.74"
|
||||||
|
fill="#898989"
|
||||||
|
></path>
|
||||||
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</svg>
|
||||||
</svg>
|
<div id="placeholder-instructions">
|
||||||
<div id="placeholder-instructions">Press and hold any button to start.</div>
|
Press and hold any button to start.
|
||||||
</div>
|
</div>
|
||||||
<div id="gamepad"></div>
|
|
||||||
<div id="instructions" style="display: none;">
|
|
||||||
<p>Press <kbd>H</kbd> or <a href="#">click here</a> to read instructions.</p>
|
|
||||||
</div>
|
|
||||||
<div id="overlay" style="display: none;">
|
|
||||||
<span id="gamepad-id">
|
|
||||||
<label for="gamepad-id-select">Gamepad</label>
|
|
||||||
<select id="gamepad-id-select" name="gamepad-id">
|
|
||||||
<option value="auto">Auto</option>
|
|
||||||
</select>
|
|
||||||
</span>
|
|
||||||
<span id="skin">
|
|
||||||
<label for="skin-select">Skin</label>
|
|
||||||
<select id="skin-select" name="skin">
|
|
||||||
<option value="auto">Auto</option>
|
|
||||||
<option value="ds4">DualShock 4</option>
|
|
||||||
<option value="dualsense">DualSense</option>
|
|
||||||
<option value="xbox-one">Xbox One</option>
|
|
||||||
<option value="debug">Debug</option>
|
|
||||||
<option value="telemetry">Telemetry (Beta)</option>
|
|
||||||
</select>
|
|
||||||
</span>
|
|
||||||
<span id="background">
|
|
||||||
<label for="background-select">Background</label>
|
|
||||||
<select id="background-select" name="background">
|
|
||||||
<option value="transparent">Transparent</option>
|
|
||||||
<option value="checkered">Checkered</option>
|
|
||||||
<option value="dimgrey">Grey</option>
|
|
||||||
<option value="black">Black</option>
|
|
||||||
<option value="white">White</option>
|
|
||||||
<option value="lime">Lime</option>
|
|
||||||
<option value="magenta">Magenta</option>
|
|
||||||
</select>
|
|
||||||
</span>
|
|
||||||
<span id="color" style="display: none;">
|
|
||||||
<label for="color-select">Color</label>
|
|
||||||
<select id="color-select" name="color">
|
|
||||||
<option value="">Default</option>
|
|
||||||
</select>
|
|
||||||
</span>
|
|
||||||
<span id="triggers" style="display: none;">
|
|
||||||
<label for="triggers-select">Triggers</label>
|
|
||||||
<select id="triggers-select" name="triggers">
|
|
||||||
<option value="opacity">Opacity</option>
|
|
||||||
<option value="meter">Meter</option>
|
|
||||||
</select>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div id="help" class="popout">
|
|
||||||
<div class="content">
|
|
||||||
<div class="close">✕</div>
|
|
||||||
|
|
||||||
<h2>Help</h2>
|
|
||||||
|
|
||||||
<h3>Instructions</h3>
|
|
||||||
<p>Press and hold any of your gamepad buttons for at least 1 second. If your gamepad is supported, it shows
|
|
||||||
up.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h3>Detected Controllers</h3>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Index</th>
|
|
||||||
<th>Controller ID</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody id="gamepad-list">
|
|
||||||
<tr>
|
|
||||||
<td colspan="2">No gamepad detected.</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<h3>Keyboard shortcuts</h3>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Key</th>
|
|
||||||
<th>Action</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<th><kbd>+</kbd></th>
|
|
||||||
<td>Zoom in</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th><kbd>-</kbd></th>
|
|
||||||
<td>Zoom out</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th><kbd>0</kbd></th>
|
|
||||||
<td>Reset zoom to 100%</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th><kbd>5</kbd></th>
|
|
||||||
<td>Adjust zoom automatically to window</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th><kbd>B</kbd></th>
|
|
||||||
<td>Change background style</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th><kbd>C</kbd></th>
|
|
||||||
<td>Change active gamepad color</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th><kbd>D</kbd></th>
|
|
||||||
<td>Toggle debug mode for active gamepad</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th><kbd>H</kbd></th>
|
|
||||||
<td>Toggle this help menu</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th><kbd>T</kbd></th>
|
|
||||||
<td>Toggle triggers meter mode</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th><kbd>Delete</kbd>, <kbd>Escape</kbd></th>
|
|
||||||
<td>Clear active gamepad</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<h3>Credits</h3>
|
|
||||||
<p>All information and source code can be found on GitHub at <a target="_blank"
|
|
||||||
href="https://github.com/e7d/gamepad-viewer">e7d/gamepad-viewer</a>.</p>
|
|
||||||
<p>DualShock 4 and Xbox One skins from <a target="_blank"
|
|
||||||
href="https://gamepadviewer.com/">gamepadviewer.com</a>.</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div id="gamepad"></div>
|
||||||
|
<div id="instructions" style="display: none">
|
||||||
|
<p>
|
||||||
|
Press <kbd>H</kbd> or <a href="#">click here</a> to read
|
||||||
|
instructions.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div id="overlay" style="display: none">
|
||||||
|
<span id="gamepad-id">
|
||||||
|
<label for="gamepad-id-select">Gamepad</label>
|
||||||
|
<select id="gamepad-id-select" name="gamepad-id">
|
||||||
|
<option value="auto">Auto</option>
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
|
<span id="skin">
|
||||||
|
<label for="skin-select">Skin</label>
|
||||||
|
<select id="skin-select" name="skin">
|
||||||
|
<option value="auto">Auto</option>
|
||||||
|
<option value="ds4">DualShock 4</option>
|
||||||
|
<option value="dualsense">DualSense</option>
|
||||||
|
<option value="xbox-one">Xbox One</option>
|
||||||
|
<option value="debug">Debug</option>
|
||||||
|
<option value="telemetry">Telemetry (Beta)</option>
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
|
<span id="background">
|
||||||
|
<label for="background-select">Background</label>
|
||||||
|
<select id="background-select" name="background">
|
||||||
|
<option value="transparent">Transparent</option>
|
||||||
|
<option value="checkered">Checkered</option>
|
||||||
|
<option value="dimgrey">Grey</option>
|
||||||
|
<option value="black">Black</option>
|
||||||
|
<option value="white">White</option>
|
||||||
|
<option value="lime">Lime</option>
|
||||||
|
<option value="magenta">Magenta</option>
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
|
<span id="color" style="display: none">
|
||||||
|
<label for="color-select">Color</label>
|
||||||
|
<select id="color-select" name="color">
|
||||||
|
<option value="">Default</option>
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
|
<span id="triggers" style="display: none">
|
||||||
|
<label for="triggers-select">Triggers</label>
|
||||||
|
<select id="triggers-select" name="triggers">
|
||||||
|
<option value="opacity">Opacity</option>
|
||||||
|
<option value="meter">Meter</option>
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div id="help" class="popout">
|
||||||
|
<div class="content">
|
||||||
|
<div class="close">✕</div>
|
||||||
|
|
||||||
<script async src="js/gamepad.js"></script>
|
<h2>Help</h2>
|
||||||
</body>
|
|
||||||
|
|
||||||
|
<h3>Instructions</h3>
|
||||||
|
<p>
|
||||||
|
Press and hold any of your gamepad buttons for at least 1
|
||||||
|
second. If your gamepad is supported, it shows up.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Detected Controllers</h3>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Index</th>
|
||||||
|
<th>Controller ID</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="gamepad-list">
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">No gamepad detected.</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h3>Keyboard shortcuts</h3>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Key</th>
|
||||||
|
<th>Action</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th><kbd>+</kbd></th>
|
||||||
|
<td>Zoom in</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th><kbd>-</kbd></th>
|
||||||
|
<td>Zoom out</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th><kbd>0</kbd></th>
|
||||||
|
<td>Reset zoom to 100%</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th><kbd>5</kbd></th>
|
||||||
|
<td>Adjust zoom automatically to window</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th><kbd>B</kbd></th>
|
||||||
|
<td>Change background style</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th><kbd>C</kbd></th>
|
||||||
|
<td>Change active gamepad color</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th><kbd>D</kbd></th>
|
||||||
|
<td>Toggle debug mode for active gamepad</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th><kbd>H</kbd></th>
|
||||||
|
<td>Toggle this help menu</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th><kbd>T</kbd></th>
|
||||||
|
<td>Toggle triggers meter mode</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th><kbd>Delete</kbd>, <kbd>Escape</kbd></th>
|
||||||
|
<td>Clear active gamepad</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h3>Credits</h3>
|
||||||
|
<p>
|
||||||
|
All information and source code can be found on GitHub at
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
href="https://github.com/e7d/gamepad-viewer"
|
||||||
|
>e7d/gamepad-viewer</a
|
||||||
|
>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
DualShock 4 and Xbox One skins from
|
||||||
|
<a target="_blank" href="https://gamepadviewer.com/"
|
||||||
|
>gamepadviewer.com</a
|
||||||
|
>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script async src="js/gamepad.js"></script>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
410
js/gamepad.js
@ -6,44 +6,58 @@
|
|||||||
class Gamepad {
|
class Gamepad {
|
||||||
REGEX = {
|
REGEX = {
|
||||||
CHROME: /^(?<name>.*) \((?:.*?Vendor: (?<vendor>[0-9a-f]{4}) Product: (?<product>[0-9a-f]{4})|(?<id>.*))\)$/i,
|
CHROME: /^(?<name>.*) \((?:.*?Vendor: (?<vendor>[0-9a-f]{4}) Product: (?<product>[0-9a-f]{4})|(?<id>.*))\)$/i,
|
||||||
FIREFOX: /^((?<vendor>[0-9a-f]{4})-(?<product>[0-9a-f]{4})-(?<name>.*))$/i,
|
FIREFOX:
|
||||||
|
/^((?<vendor>[0-9a-f]{4})-(?<product>[0-9a-f]{4})-(?<name>.*))$/i,
|
||||||
OTHER: /^(?<name>.*)$/i,
|
OTHER: /^(?<name>.*)$/i,
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance of Gamepad.
|
* Creates an instance of Gamepad.
|
||||||
*/
|
*/
|
||||||
constructor() {
|
constructor() {
|
||||||
// cached DOM references
|
// cached DOM references
|
||||||
this.$body = document.querySelector('body');
|
this.$body = document.querySelector("body");
|
||||||
this.$instructions = document.querySelector('#instructions');
|
this.$instructions = document.querySelector("#instructions");
|
||||||
this.$instructionsLink = this.$instructions.querySelector('a');
|
this.$instructionsLink = this.$instructions.querySelector("a");
|
||||||
this.$placeholder = document.querySelector('#placeholder');
|
this.$placeholder = document.querySelector("#placeholder");
|
||||||
this.$gamepad = document.querySelector('#gamepad');
|
this.$gamepad = document.querySelector("#gamepad");
|
||||||
this.$overlay = document.querySelector('#overlay');
|
this.$overlay = document.querySelector("#overlay");
|
||||||
this.$gamepadSelect = document.querySelector('select[name=gamepad-id]');
|
this.$gamepadSelect = document.querySelector("select[name=gamepad-id]");
|
||||||
this.$skinSelect = document.querySelector('select[name=skin]');
|
this.$skinSelect = document.querySelector("select[name=skin]");
|
||||||
this.$backgroundSelect = document.querySelector('select[name=background]');
|
this.$backgroundSelect = document.querySelector(
|
||||||
this.$colorOverlay = this.$overlay.querySelector('#color');
|
"select[name=background]",
|
||||||
this.$colorSelect = this.$colorOverlay.querySelector('select[name=color]');
|
);
|
||||||
this.$triggersOverlay = this.$overlay.querySelector('#triggers');
|
this.$colorOverlay = this.$overlay.querySelector("#color");
|
||||||
this.$triggersSelect = this.$triggersOverlay.querySelector('select[name=triggers]');
|
this.$colorSelect =
|
||||||
this.$helpPopout = document.querySelector('#help.popout');
|
this.$colorOverlay.querySelector("select[name=color]");
|
||||||
this.$helpPopoutClose = this.$helpPopout.querySelector('.close');
|
this.$triggersOverlay = this.$overlay.querySelector("#triggers");
|
||||||
this.$gamepadList = document.querySelector('#gamepad-list');
|
this.$triggersSelect = this.$triggersOverlay.querySelector(
|
||||||
|
"select[name=triggers]",
|
||||||
|
);
|
||||||
|
this.$helpPopout = document.querySelector("#help.popout");
|
||||||
|
this.$helpPopoutClose = this.$helpPopout.querySelector(".close");
|
||||||
|
this.$gamepadList = document.querySelector("#gamepad-list");
|
||||||
|
|
||||||
// ensure the GamePad API is available on this browser
|
// ensure the GamePad API is available on this browser
|
||||||
this.assertGamepadAPI();
|
this.assertGamepadAPI();
|
||||||
|
|
||||||
// overlay selectors
|
// overlay selectors
|
||||||
this.backgrounds = [
|
this.backgrounds = [
|
||||||
{ name: 'transparent', backgroundColor: 'transparent', textColor: 'black' },
|
{
|
||||||
{ name: 'checkered', backgroundColor: 'url(css/transparent-bg.png)', textColor: 'black' },
|
name: "transparent",
|
||||||
{ name: 'dimgrey', backgroundColor: 'dimgrey', textColor: 'black' },
|
backgroundColor: "transparent",
|
||||||
{ name: 'black', backgroundColor: 'black', textColor: 'white' },
|
textColor: "black",
|
||||||
{ name: 'white', backgroundColor: 'white', textColor: 'black' },
|
},
|
||||||
{ name: 'lime', backgroundColor: 'lime', textColor: 'black' },
|
{
|
||||||
{ name: 'magenta', backgroundColor: 'magenta', textColor: 'black' },
|
name: "checkered",
|
||||||
|
backgroundColor: "url(css/transparent-bg.png)",
|
||||||
|
textColor: "black",
|
||||||
|
},
|
||||||
|
{ name: "dimgrey", backgroundColor: "dimgrey", textColor: "black" },
|
||||||
|
{ name: "black", backgroundColor: "black", textColor: "white" },
|
||||||
|
{ name: "white", backgroundColor: "white", textColor: "black" },
|
||||||
|
{ name: "lime", backgroundColor: "lime", textColor: "black" },
|
||||||
|
{ name: "magenta", backgroundColor: "magenta", textColor: "black" },
|
||||||
];
|
];
|
||||||
this.initOverlaySelectors();
|
this.initOverlaySelectors();
|
||||||
|
|
||||||
@ -53,19 +67,19 @@ class Gamepad {
|
|||||||
// See: https://html5gamepad.com/codes
|
// See: https://html5gamepad.com/codes
|
||||||
debug: {
|
debug: {
|
||||||
id: /debug/,
|
id: /debug/,
|
||||||
name: 'Debug',
|
name: "Debug",
|
||||||
},
|
},
|
||||||
ds4: {
|
ds4: {
|
||||||
id: /05c4|09cc|0104|046d|0810|2563/, // 05c4,09cc,0104 = DS4 controllers product codes, 046d,0810,2563 = PS-like controllers vendor codes
|
id: /05c4|09cc|0104|046d|0810|2563/, // 05c4,09cc,0104 = DS4 controllers product codes, 046d,0810,2563 = PS-like controllers vendor codes
|
||||||
name: 'DualShock 4',
|
name: "DualShock 4",
|
||||||
colors: ['black', 'white', 'red', 'blue'],
|
colors: ["black", "white", "red", "blue"],
|
||||||
triggers: true,
|
triggers: true,
|
||||||
zoom: true,
|
zoom: true,
|
||||||
},
|
},
|
||||||
dualsense: {
|
dualsense: {
|
||||||
id: /0ce6/, // 0ce6 = DualSense controller product code
|
id: /0ce6/, // 0ce6 = DualSense controller product code
|
||||||
name: 'DualSense',
|
name: "DualSense",
|
||||||
colors: ['white', 'black'],
|
colors: ["white", "black"],
|
||||||
triggers: true,
|
triggers: true,
|
||||||
zoom: true,
|
zoom: true,
|
||||||
},
|
},
|
||||||
@ -91,13 +105,13 @@ class Gamepad {
|
|||||||
// },
|
// },
|
||||||
telemetry: {
|
telemetry: {
|
||||||
id: /telemetry/,
|
id: /telemetry/,
|
||||||
name: 'Telemetry',
|
name: "Telemetry",
|
||||||
zoom: true
|
zoom: true,
|
||||||
},
|
},
|
||||||
'xbox-one': {
|
"xbox-one": {
|
||||||
id: /045e|xinput|XInput/, // 045e = Microsoft vendor code, xinput = standard Windows controller
|
id: /045e|xinput|XInput/, // 045e = Microsoft vendor code, xinput = standard Windows controller
|
||||||
name: 'Xbox One',
|
name: "Xbox One",
|
||||||
colors: ['black', 'white'],
|
colors: ["black", "white"],
|
||||||
triggers: true,
|
triggers: true,
|
||||||
zoom: true,
|
zoom: true,
|
||||||
},
|
},
|
||||||
@ -124,7 +138,7 @@ class Gamepad {
|
|||||||
this.colorIndex = null;
|
this.colorIndex = null;
|
||||||
this.colorName = null;
|
this.colorName = null;
|
||||||
this.useMeterTriggers = false;
|
this.useMeterTriggers = false;
|
||||||
this.zoomMode = 'auto';
|
this.zoomMode = "auto";
|
||||||
this.zoomLevel = 1;
|
this.zoomLevel = 1;
|
||||||
this.mapping = {
|
this.mapping = {
|
||||||
buttons: [],
|
buttons: [],
|
||||||
@ -132,44 +146,50 @@ class Gamepad {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// listen for gamepad related events
|
// listen for gamepad related events
|
||||||
this.haveEvents = 'GamepadEvent' in window;
|
this.haveEvents = "GamepadEvent" in window;
|
||||||
if (this.haveEvents) {
|
if (this.haveEvents) {
|
||||||
window.addEventListener(
|
window.addEventListener(
|
||||||
'gamepadconnected',
|
"gamepadconnected",
|
||||||
this.onGamepadConnect.bind(this)
|
this.onGamepadConnect.bind(this),
|
||||||
);
|
);
|
||||||
window.addEventListener(
|
window.addEventListener(
|
||||||
'gamepaddisconnected',
|
"gamepaddisconnected",
|
||||||
this.onGamepadDisconnect.bind(this)
|
this.onGamepadDisconnect.bind(this),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// listen for mouse move events
|
// listen for mouse move events
|
||||||
window.addEventListener('mousemove', this.onMouseMove.bind(this));
|
window.addEventListener("mousemove", this.onMouseMove.bind(this));
|
||||||
// listen for keyboard events
|
// listen for keyboard events
|
||||||
window.addEventListener('keydown', this.onKeyDown.bind(this));
|
window.addEventListener("keydown", this.onKeyDown.bind(this));
|
||||||
// listen for keyboard events
|
// listen for keyboard events
|
||||||
window.addEventListener('resize', this.onResize.bind(this));
|
window.addEventListener("resize", this.onResize.bind(this));
|
||||||
|
|
||||||
// bind a gamepads scan
|
// bind a gamepads scan
|
||||||
window.setInterval(this.scan.bind(this), this.scanDelay);
|
window.setInterval(this.scan.bind(this), this.scanDelay);
|
||||||
|
|
||||||
// change the type if specified
|
// change the type if specified
|
||||||
const skin = this.getUrlParam('type');
|
const skin = this.getUrlParam("type");
|
||||||
if (skin) {
|
if (skin) {
|
||||||
this.changeSkin(skin);
|
this.changeSkin(skin);
|
||||||
}
|
}
|
||||||
|
|
||||||
// change the background if specified
|
// change the background if specified
|
||||||
const background = this.getUrlParam('background');
|
const background = this.getUrlParam("background");
|
||||||
if (background) this.changeBackground(background);
|
if (background) this.changeBackground(background);
|
||||||
|
|
||||||
// by default, enqueue a delayed display of the placeholder animation
|
// by default, enqueue a delayed display of the placeholder animation
|
||||||
this.displayPlaceholder();
|
this.displayPlaceholder();
|
||||||
|
|
||||||
// listen for click events
|
// listen for click events
|
||||||
this.$instructionsLink.addEventListener('click', this.toggleHelp.bind(this));
|
this.$instructionsLink.addEventListener(
|
||||||
this.$helpPopoutClose.addEventListener('click', this.toggleHelp.bind(this));
|
"click",
|
||||||
|
this.toggleHelp.bind(this),
|
||||||
|
);
|
||||||
|
this.$helpPopoutClose.addEventListener(
|
||||||
|
"click",
|
||||||
|
this.toggleHelp.bind(this),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -179,11 +199,11 @@ class Gamepad {
|
|||||||
const getGamepadsFn = navigator.getGamepads
|
const getGamepadsFn = navigator.getGamepads
|
||||||
? () => navigator.getGamepads()
|
? () => navigator.getGamepads()
|
||||||
: navigator.webkitGetGamepads
|
: navigator.webkitGetGamepads
|
||||||
? () => navigator.webkitGetGamepads()
|
? () => navigator.webkitGetGamepads()
|
||||||
: null;
|
: null;
|
||||||
if (!getGamepadsFn) {
|
if (!getGamepadsFn) {
|
||||||
this.$body.classList.add('unsupported');
|
this.$body.classList.add("unsupported");
|
||||||
throw new Error('Unsupported gamepad API');
|
throw new Error("Unsupported gamepad API");
|
||||||
}
|
}
|
||||||
this.getNavigatorGamepads = getGamepadsFn;
|
this.getNavigatorGamepads = getGamepadsFn;
|
||||||
}
|
}
|
||||||
@ -192,20 +212,20 @@ class Gamepad {
|
|||||||
* Initialises the overlay selectors
|
* Initialises the overlay selectors
|
||||||
*/
|
*/
|
||||||
initOverlaySelectors() {
|
initOverlaySelectors() {
|
||||||
this.$gamepadSelect.addEventListener('change', () =>
|
this.$gamepadSelect.addEventListener("change", () =>
|
||||||
this.changeGamepad(this.$gamepadSelect.value)
|
this.changeGamepad(this.$gamepadSelect.value),
|
||||||
);
|
);
|
||||||
this.$skinSelect.addEventListener('change', () =>
|
this.$skinSelect.addEventListener("change", () =>
|
||||||
this.changeSkin(this.$skinSelect.value)
|
this.changeSkin(this.$skinSelect.value),
|
||||||
);
|
);
|
||||||
this.$backgroundSelect.addEventListener('change', () =>
|
this.$backgroundSelect.addEventListener("change", () =>
|
||||||
this.changeBackground(this.$backgroundSelect.value)
|
this.changeBackground(this.$backgroundSelect.value),
|
||||||
);
|
);
|
||||||
this.$colorSelect.addEventListener('change', () =>
|
this.$colorSelect.addEventListener("change", () =>
|
||||||
this.changeGamepadColor(this.$colorSelect.value)
|
this.changeGamepadColor(this.$colorSelect.value),
|
||||||
);
|
);
|
||||||
this.$triggersSelect.addEventListener('change', () =>
|
this.$triggersSelect.addEventListener("change", () =>
|
||||||
this.toggleTriggers(this.$triggersSelect.value === 'meter')
|
this.toggleTriggers(this.$triggersSelect.value === "meter"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,8 +235,8 @@ class Gamepad {
|
|||||||
* @param {HTMLElement} $element
|
* @param {HTMLElement} $element
|
||||||
*/
|
*/
|
||||||
show($element) {
|
show($element) {
|
||||||
$element.style.removeProperty('display');
|
$element.style.removeProperty("display");
|
||||||
$element.classList.remove('fadeIn', 'fadeOut');
|
$element.classList.remove("fadeIn", "fadeOut");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -225,8 +245,8 @@ class Gamepad {
|
|||||||
* @param {HTMLElement} $element
|
* @param {HTMLElement} $element
|
||||||
*/
|
*/
|
||||||
hide($element) {
|
hide($element) {
|
||||||
$element.style.setProperty('display', 'none');
|
$element.style.setProperty("display", "none");
|
||||||
$element.classList.remove('fadeIn', 'fadeOut');
|
$element.classList.remove("fadeIn", "fadeOut");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -235,9 +255,9 @@ class Gamepad {
|
|||||||
* @param {HTMLElement} $element
|
* @param {HTMLElement} $element
|
||||||
*/
|
*/
|
||||||
fadeIn($element) {
|
fadeIn($element) {
|
||||||
$element.style.removeProperty('display');
|
$element.style.removeProperty("display");
|
||||||
$element.classList.remove('fadeOut');
|
$element.classList.remove("fadeOut");
|
||||||
$element.classList.add('fadeIn');
|
$element.classList.add("fadeIn");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -246,9 +266,9 @@ class Gamepad {
|
|||||||
* @param {HTMLElement} $element
|
* @param {HTMLElement} $element
|
||||||
*/
|
*/
|
||||||
fadeOut($element) {
|
fadeOut($element) {
|
||||||
$element.style.removeProperty('display');
|
$element.style.removeProperty("display");
|
||||||
$element.classList.remove('fadeIn');
|
$element.classList.remove("fadeIn");
|
||||||
$element.classList.add('fadeOut');
|
$element.classList.add("fadeOut");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -283,7 +303,7 @@ class Gamepad {
|
|||||||
// hide instructions animation if no gamepad is active after X ms
|
// hide instructions animation if no gamepad is active after X ms
|
||||||
this.instructionsTimeout = window.setTimeout(
|
this.instructionsTimeout = window.setTimeout(
|
||||||
() => this.fadeOut(this.$instructions),
|
() => this.fadeOut(this.$instructions),
|
||||||
this.instructionsDelay
|
this.instructionsDelay,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,7 +339,7 @@ class Gamepad {
|
|||||||
// hide placeholder animation if no gamepad is active after X ms
|
// hide placeholder animation if no gamepad is active after X ms
|
||||||
this.placeholderTimeout = window.setTimeout(
|
this.placeholderTimeout = window.setTimeout(
|
||||||
() => this.fadeOut(this.$placeholder),
|
() => this.fadeOut(this.$placeholder),
|
||||||
this.placeholderDelay
|
this.placeholderDelay,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -352,7 +372,7 @@ class Gamepad {
|
|||||||
// hide overlay animation if no gamepad is active after X ms
|
// hide overlay animation if no gamepad is active after X ms
|
||||||
this.overlayTimeout = window.setTimeout(
|
this.overlayTimeout = window.setTimeout(
|
||||||
() => this.fadeOut(this.$overlay),
|
() => this.fadeOut(this.$overlay),
|
||||||
this.overlayDelay
|
this.overlayDelay,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -369,14 +389,14 @@ class Gamepad {
|
|||||||
if (firefoxResults) return firefoxResults.groups;
|
if (firefoxResults) return firefoxResults.groups;
|
||||||
const otherResults = this.REGEX.OTHER.exec(id);
|
const otherResults = this.REGEX.OTHER.exec(id);
|
||||||
if (otherResults) return otherResults.groups;
|
if (otherResults) return otherResults.groups;
|
||||||
return { name: id, vendor: '', product: '' };
|
return { name: id, vendor: "", product: "" };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the list of connected gamepads in the overlay
|
* Updates the list of connected gamepads in the overlay
|
||||||
*/
|
*/
|
||||||
updateGamepadList() {
|
updateGamepadList() {
|
||||||
for (const $entry of this.$gamepadSelect.querySelectorAll('.entry')) {
|
for (const $entry of this.$gamepadSelect.querySelectorAll(".entry")) {
|
||||||
$entry.remove();
|
$entry.remove();
|
||||||
}
|
}
|
||||||
const $options = [];
|
const $options = [];
|
||||||
@ -385,10 +405,10 @@ class Gamepad {
|
|||||||
if (!gamepad) continue;
|
if (!gamepad) continue;
|
||||||
const { name } = this.toGamepadInfo(gamepad.id);
|
const { name } = this.toGamepadInfo(gamepad.id);
|
||||||
$options.push(
|
$options.push(
|
||||||
`<option class='entry' value='${gamepad.id}'>${name}</option>`
|
`<option class='entry' value='${gamepad.id}'>${name}</option>`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
this.$gamepadSelect.innerHTML += $options.join('');
|
this.$gamepadSelect.innerHTML += $options.join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -407,9 +427,10 @@ class Gamepad {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const colorOptions = colors.map(
|
const colorOptions = colors.map(
|
||||||
(color) => `<option value='${color}'>${color.charAt(0).toUpperCase()}${color.slice(1)}</option>`
|
(color) =>
|
||||||
|
`<option value='${color}'>${color.charAt(0).toUpperCase()}${color.slice(1)}</option>`,
|
||||||
);
|
);
|
||||||
this.$colorSelect.innerHTML = colorOptions.join('');
|
this.$colorSelect.innerHTML = colorOptions.join("");
|
||||||
this.show(this.$colorOverlay);
|
this.show(this.$colorOverlay);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -461,7 +482,7 @@ class Gamepad {
|
|||||||
|
|
||||||
if (e.gamepad.index === this.index) {
|
if (e.gamepad.index === this.index) {
|
||||||
// display a disconnection indicator
|
// display a disconnection indicator
|
||||||
this.$gamepad.classList.add('disconnected');
|
this.$gamepad.classList.add("disconnected");
|
||||||
this.disconnectedIndex = e.gamepad.index;
|
this.disconnectedIndex = e.gamepad.index;
|
||||||
|
|
||||||
// refresh gamepad list on help, if displayed
|
// refresh gamepad list on help, if displayed
|
||||||
@ -485,43 +506,43 @@ class Gamepad {
|
|||||||
*/
|
*/
|
||||||
onKeyDown(e) {
|
onKeyDown(e) {
|
||||||
switch (e.code) {
|
switch (e.code) {
|
||||||
case 'Delete':
|
case "Delete":
|
||||||
case 'Escape':
|
case "Escape":
|
||||||
this.clear();
|
this.clear();
|
||||||
this.displayPlaceholder();
|
this.displayPlaceholder();
|
||||||
break;
|
break;
|
||||||
case 'KeyB':
|
case "KeyB":
|
||||||
this.changeBackground();
|
this.changeBackground();
|
||||||
break;
|
break;
|
||||||
case 'KeyC':
|
case "KeyC":
|
||||||
this.changeGamepadColor();
|
this.changeGamepadColor();
|
||||||
break;
|
break;
|
||||||
case 'KeyD':
|
case "KeyD":
|
||||||
this.toggleDebug();
|
this.toggleDebug();
|
||||||
break;
|
break;
|
||||||
case 'KeyG':
|
case "KeyG":
|
||||||
this.toggleGamepadType();
|
this.toggleGamepadType();
|
||||||
break;
|
break;
|
||||||
case 'KeyH':
|
case "KeyH":
|
||||||
this.toggleHelp();
|
this.toggleHelp();
|
||||||
break;
|
break;
|
||||||
case 'KeyT':
|
case "KeyT":
|
||||||
this.toggleTriggers();
|
this.toggleTriggers();
|
||||||
break;
|
break;
|
||||||
case 'NumpadAdd':
|
case "NumpadAdd":
|
||||||
case 'Equal':
|
case "Equal":
|
||||||
this.changeZoom('+');
|
this.changeZoom("+");
|
||||||
break;
|
break;
|
||||||
case 'NumpadSubtract':
|
case "NumpadSubtract":
|
||||||
case 'Minus':
|
case "Minus":
|
||||||
this.changeZoom('-');
|
this.changeZoom("-");
|
||||||
break;
|
break;
|
||||||
case 'Numpad5':
|
case "Numpad5":
|
||||||
case 'Digit5':
|
case "Digit5":
|
||||||
this.changeZoom('auto');
|
this.changeZoom("auto");
|
||||||
break;
|
break;
|
||||||
case 'Numpad0':
|
case "Numpad0":
|
||||||
case 'Digit0':
|
case "Digit0":
|
||||||
this.changeZoom(0);
|
this.changeZoom(0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -531,7 +552,7 @@ class Gamepad {
|
|||||||
* Handles the keyboard 'keydown' event
|
* Handles the keyboard 'keydown' event
|
||||||
*/
|
*/
|
||||||
onResize() {
|
onResize() {
|
||||||
if (this.zoomMode === 'auto') this.changeZoom('auto');
|
if (this.zoomMode === "auto") this.changeZoom("auto");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -554,15 +575,18 @@ class Gamepad {
|
|||||||
for (let key = 0; key < this.gamepads.length; key++) {
|
for (let key = 0; key < this.gamepads.length; key++) {
|
||||||
const gamepad = this.gamepads[key];
|
const gamepad = this.gamepads[key];
|
||||||
if (!gamepad) continue;
|
if (!gamepad) continue;
|
||||||
$tbody.push(`<tr><td>${gamepad.index}</td><td>${gamepad.id}</td></tr>`);
|
$tbody.push(
|
||||||
|
`<tr><td>${gamepad.index}</td><td>${gamepad.id}</td></tr>`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($tbody.length === 0) {
|
if ($tbody.length === 0) {
|
||||||
this.$gamepadList.innerHTML = '<tr><td colspan="2">No gamepad detected.</td></tr>';
|
this.$gamepadList.innerHTML =
|
||||||
|
'<tr><td colspan="2">No gamepad detected.</td></tr>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$gamepadList.innerHTML = $tbody.join('');
|
this.$gamepadList.innerHTML = $tbody.join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -581,12 +605,12 @@ class Gamepad {
|
|||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
getType(gamepad) {
|
getType(gamepad) {
|
||||||
const type = this.getUrlParam('type');
|
const type = this.getUrlParam("type");
|
||||||
|
|
||||||
// if the debug option is active, use the associated template
|
// if the debug option is active, use the associated template
|
||||||
if (type === 'debug') this.debug = true;
|
if (type === "debug") this.debug = true;
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
return 'debug';
|
return "debug";
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the gamepad type is set through params, apply it
|
// if the gamepad type is set through params, apply it
|
||||||
@ -601,7 +625,7 @@ class Gamepad {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'debug';
|
return "debug";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -624,13 +648,14 @@ class Gamepad {
|
|||||||
if (
|
if (
|
||||||
null !== this.disconnectedIndex &&
|
null !== this.disconnectedIndex &&
|
||||||
index !== this.disconnectedIndex
|
index !== this.disconnectedIndex
|
||||||
) continue;
|
)
|
||||||
|
continue;
|
||||||
|
|
||||||
const gamepad = this.gamepads[index];
|
const gamepad = this.gamepads[index];
|
||||||
if (!gamepad) continue;
|
if (!gamepad) continue;
|
||||||
|
|
||||||
// check the parameters for a selected gamepad
|
// check the parameters for a selected gamepad
|
||||||
const gamepadId = this.getUrlParam('gamepad');
|
const gamepadId = this.getUrlParam("gamepad");
|
||||||
if (gamepadId === gamepad.id) {
|
if (gamepadId === gamepad.id) {
|
||||||
this.map(gamepad.index);
|
this.map(gamepad.index);
|
||||||
return;
|
return;
|
||||||
@ -658,7 +683,7 @@ class Gamepad {
|
|||||||
strongMagnitude: 0.2,
|
strongMagnitude: 0.2,
|
||||||
weakMagnitude: 1,
|
weakMagnitude: 1,
|
||||||
startDelay: 0,
|
startDelay: 0,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -675,7 +700,7 @@ class Gamepad {
|
|||||||
*/
|
*/
|
||||||
map(index) {
|
map(index) {
|
||||||
// ensure a gamepad need to be mapped
|
// ensure a gamepad need to be mapped
|
||||||
if ('undefined' === typeof index) return;
|
if ("undefined" === typeof index) return;
|
||||||
|
|
||||||
// hide the help messages
|
// hide the help messages
|
||||||
this.hideInstructions(true);
|
this.hideInstructions(true);
|
||||||
@ -684,7 +709,7 @@ class Gamepad {
|
|||||||
// update local references
|
// update local references
|
||||||
this.index = index;
|
this.index = index;
|
||||||
this.disconnectedIndex = null;
|
this.disconnectedIndex = null;
|
||||||
this.$gamepad.classList.remove('disconnected');
|
this.$gamepad.classList.remove("disconnected");
|
||||||
const gamepad = this.getActive();
|
const gamepad = this.getActive();
|
||||||
|
|
||||||
// ensure that a gamepad was actually found for this index
|
// ensure that a gamepad was actually found for this index
|
||||||
@ -722,8 +747,12 @@ class Gamepad {
|
|||||||
*/
|
*/
|
||||||
async toHash(value) {
|
async toHash(value) {
|
||||||
return crypto.subtle
|
return crypto.subtle
|
||||||
.digest('SHA-1', new TextEncoder().encode(value))
|
.digest("SHA-1", new TextEncoder().encode(value))
|
||||||
.then(ab => encodeURIComponent(String.fromCharCode.apply(null, new Uint8Array(ab))));
|
.then((ab) =>
|
||||||
|
encodeURIComponent(
|
||||||
|
String.fromCharCode.apply(null, new Uint8Array(ab)),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -746,9 +775,9 @@ class Gamepad {
|
|||||||
this.colorIndex = null;
|
this.colorIndex = null;
|
||||||
this.colorName = null;
|
this.colorName = null;
|
||||||
this.zoomLevel = 1;
|
this.zoomLevel = 1;
|
||||||
this.$gamepad.innerHTML = '';
|
this.$gamepad.innerHTML = "";
|
||||||
this.$gamepad.classList.remove('fadeIn');
|
this.$gamepad.classList.remove("fadeIn");
|
||||||
this.$gamepadSelect.value = 'auto';
|
this.$gamepadSelect.value = "auto";
|
||||||
this.updateColors();
|
this.updateColors();
|
||||||
this.updateTriggers();
|
this.updateTriggers();
|
||||||
this.clearUrlParams();
|
this.clearUrlParams();
|
||||||
@ -758,12 +787,12 @@ class Gamepad {
|
|||||||
* Loads the template script and stylesheet
|
* Loads the template script and stylesheet
|
||||||
*/
|
*/
|
||||||
loadTemplateAssets() {
|
loadTemplateAssets() {
|
||||||
const link = document.createElement('link');
|
const link = document.createElement("link");
|
||||||
link.rel = 'stylesheet';
|
link.rel = "stylesheet";
|
||||||
link.href = `templates/${this.type}/template.css`;
|
link.href = `templates/${this.type}/template.css`;
|
||||||
this.$gamepad.appendChild(link);
|
this.$gamepad.appendChild(link);
|
||||||
|
|
||||||
const script = document.createElement('script');
|
const script = document.createElement("script");
|
||||||
script.async = true;
|
script.async = true;
|
||||||
script.src = `templates/${this.type}/template.js`;
|
script.src = `templates/${this.type}/template.js`;
|
||||||
script.onload = () => {
|
script.onload = () => {
|
||||||
@ -772,7 +801,7 @@ class Gamepad {
|
|||||||
|
|
||||||
// enqueue the initial display refresh
|
// enqueue the initial display refresh
|
||||||
this.startTemplate();
|
this.startTemplate();
|
||||||
}
|
};
|
||||||
this.$gamepad.appendChild(script);
|
this.$gamepad.appendChild(script);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -781,7 +810,7 @@ class Gamepad {
|
|||||||
*/
|
*/
|
||||||
loadTemplate() {
|
loadTemplate() {
|
||||||
// hide the gamepad while we prepare it
|
// hide the gamepad while we prepare it
|
||||||
this.$gamepad.style.setProperty('display', 'none');
|
this.$gamepad.style.setProperty("display", "none");
|
||||||
|
|
||||||
fetch(`templates/${this.type}/template.html`)
|
fetch(`templates/${this.type}/template.html`)
|
||||||
.then((response) => response.text())
|
.then((response) => response.text())
|
||||||
@ -794,13 +823,15 @@ class Gamepad {
|
|||||||
const identifier = this.identifiers[this.type];
|
const identifier = this.identifiers[this.type];
|
||||||
// - color
|
// - color
|
||||||
if (identifier.colors) {
|
if (identifier.colors) {
|
||||||
this.changeGamepadColor(this.getUrlParam('color'));
|
this.changeGamepadColor(this.getUrlParam("color"));
|
||||||
} else {
|
} else {
|
||||||
this.updateUrlParams({ color: undefined });
|
this.updateUrlParams({ color: undefined });
|
||||||
}
|
}
|
||||||
// - triggers mode
|
// - triggers mode
|
||||||
if (identifier.triggers) {
|
if (identifier.triggers) {
|
||||||
this.toggleTriggers(this.getUrlParam('triggers') === 'meter');
|
this.toggleTriggers(
|
||||||
|
this.getUrlParam("triggers") === "meter",
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
this.updateUrlParams({ triggers: undefined });
|
this.updateUrlParams({ triggers: undefined });
|
||||||
}
|
}
|
||||||
@ -808,19 +839,19 @@ class Gamepad {
|
|||||||
if (identifier.zoom) {
|
if (identifier.zoom) {
|
||||||
window.setTimeout(() =>
|
window.setTimeout(() =>
|
||||||
this.changeZoom(
|
this.changeZoom(
|
||||||
this.type === 'debug'
|
this.type === "debug"
|
||||||
? 'auto'
|
? "auto"
|
||||||
: this.getUrlParam('zoom') || 'auto'
|
: this.getUrlParam("zoom") || "auto",
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this.updateUrlParams({ zoom: undefined });
|
this.updateUrlParams({ zoom: undefined });
|
||||||
}
|
}
|
||||||
|
|
||||||
// once fully loaded, display the gamepad
|
// once fully loaded, display the gamepad
|
||||||
this.$gamepad.style.removeProperty('display');
|
this.$gamepad.style.removeProperty("display");
|
||||||
this.$gamepad.classList.remove('fadeOut');
|
this.$gamepad.classList.remove("fadeOut");
|
||||||
this.$gamepad.classList.add('fadeIn');
|
this.$gamepad.classList.add("fadeIn");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -834,14 +865,23 @@ class Gamepad {
|
|||||||
// save the buttons mapping of this template
|
// save the buttons mapping of this template
|
||||||
this.mapping.buttons = activeGamepad.buttons.map((_, index) => {
|
this.mapping.buttons = activeGamepad.buttons.map((_, index) => {
|
||||||
const $button = document.querySelector(`[data-button='${index}']`);
|
const $button = document.querySelector(`[data-button='${index}']`);
|
||||||
return { $button, button: { pressed: null, touched: null, value: null } };
|
return {
|
||||||
|
$button,
|
||||||
|
button: { pressed: null, touched: null, value: null },
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// save the axes mapping of this template
|
// save the axes mapping of this template
|
||||||
this.mapping.axes = activeGamepad.axes.map((_, index) => {
|
this.mapping.axes = activeGamepad.axes.map((_, index) => {
|
||||||
const { $axis, attribute } = ['data-axis', 'data-axis-x', 'data-axis-y'].reduce((acc, attribute) => {
|
const { $axis, attribute } = [
|
||||||
|
"data-axis",
|
||||||
|
"data-axis-x",
|
||||||
|
"data-axis-y",
|
||||||
|
].reduce((acc, attribute) => {
|
||||||
if (acc.$axis) return acc;
|
if (acc.$axis) return acc;
|
||||||
const $axis = document.querySelector(`[${attribute}='${index}']`);
|
const $axis = document.querySelector(
|
||||||
|
`[${attribute}='${index}']`,
|
||||||
|
);
|
||||||
return $axis ? { $axis, attribute, axis: null } : acc;
|
return $axis ? { $axis, attribute, axis: null } : acc;
|
||||||
}, {});
|
}, {});
|
||||||
return { $axis, attribute };
|
return { $axis, attribute };
|
||||||
@ -870,7 +910,8 @@ class Gamepad {
|
|||||||
*/
|
*/
|
||||||
pollStatus(force = false) {
|
pollStatus(force = false) {
|
||||||
// ensure that a gamepad is currently active
|
// ensure that a gamepad is currently active
|
||||||
if (this.index === null || this.index === this.disconnectedIndex) return;
|
if (this.index === null || this.index === this.disconnectedIndex)
|
||||||
|
return;
|
||||||
|
|
||||||
// enqueue the next refresh
|
// enqueue the next refresh
|
||||||
window.requestAnimationFrame(this.pollStatus.bind(this));
|
window.requestAnimationFrame(this.pollStatus.bind(this));
|
||||||
@ -907,12 +948,13 @@ class Gamepad {
|
|||||||
updatedButton.touched !== button.touched ||
|
updatedButton.touched !== button.touched ||
|
||||||
updatedButton.value !== button.value
|
updatedButton.value !== button.value
|
||||||
) {
|
) {
|
||||||
$button.setAttribute('data-pressed', updatedButton.pressed);
|
$button.setAttribute("data-pressed", updatedButton.pressed);
|
||||||
$button.setAttribute('data-touched', updatedButton.touched);
|
$button.setAttribute("data-touched", updatedButton.touched);
|
||||||
$button.setAttribute('data-value', updatedButton.value);
|
$button.setAttribute("data-value", updatedButton.value);
|
||||||
|
|
||||||
// ensure we have a button updater callback and hook the template defined button update method
|
// ensure we have a button updater callback and hook the template defined button update method
|
||||||
if ('function' === typeof this.updateButton) this.updateButton($button, updatedButton);
|
if ("function" === typeof this.updateButton)
|
||||||
|
this.updateButton($button, updatedButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
// save the updated button
|
// save the updated button
|
||||||
@ -935,10 +977,14 @@ class Gamepad {
|
|||||||
|
|
||||||
// update the display value
|
// update the display value
|
||||||
if (updatedAxis !== axis) {
|
if (updatedAxis !== axis) {
|
||||||
$axis.setAttribute(attribute.replace('-axis', '-value'), updatedAxis);
|
$axis.setAttribute(
|
||||||
|
attribute.replace("-axis", "-value"),
|
||||||
|
updatedAxis,
|
||||||
|
);
|
||||||
|
|
||||||
// ensure we have an axis updater callback and hook the template defined axis update method
|
// ensure we have an axis updater callback and hook the template defined axis update method
|
||||||
if ('function' === typeof this.updateAxis) this.updateAxis($axis, attribute, updatedAxis);
|
if ("function" === typeof this.updateAxis)
|
||||||
|
this.updateAxis($axis, attribute, updatedAxis);
|
||||||
}
|
}
|
||||||
|
|
||||||
// save the updated button
|
// save the updated button
|
||||||
@ -953,10 +999,10 @@ class Gamepad {
|
|||||||
*/
|
*/
|
||||||
changeGamepad(id) {
|
changeGamepad(id) {
|
||||||
// get the index corresponding to the identifier of the gamepad
|
// get the index corresponding to the identifier of the gamepad
|
||||||
const index = this.gamepads.findIndex(g => g && id === g.id);
|
const index = this.gamepads.findIndex((g) => g && id === g.id);
|
||||||
|
|
||||||
// set the selected gamepad
|
// set the selected gamepad
|
||||||
this.updateUrlParams({ gamepad: id !== 'auto' ? id : undefined });
|
this.updateUrlParams({ gamepad: id !== "auto" ? id : undefined });
|
||||||
index === -1 ? this.clear() : this.map(index);
|
index === -1 ? this.clear() : this.map(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -973,8 +1019,8 @@ class Gamepad {
|
|||||||
this.$skinSelect.value = skin;
|
this.$skinSelect.value = skin;
|
||||||
|
|
||||||
// set the selected skin
|
// set the selected skin
|
||||||
this.debug = skin === 'debug';
|
this.debug = skin === "debug";
|
||||||
this.updateUrlParams({ type: skin !== 'auto' ? skin : undefined });
|
this.updateUrlParams({ type: skin !== "auto" ? skin : undefined });
|
||||||
this.map(this.index);
|
this.map(this.index);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -984,20 +1030,23 @@ class Gamepad {
|
|||||||
* @param {string|number|undefined} indexOrName
|
* @param {string|number|undefined} indexOrName
|
||||||
*/
|
*/
|
||||||
changeBackground(indexOrName) {
|
changeBackground(indexOrName) {
|
||||||
if ('undefined' === typeof indexOrName) {
|
if ("undefined" === typeof indexOrName) {
|
||||||
this.backgroundIndex++;
|
this.backgroundIndex++;
|
||||||
if (this.backgroundIndex > this.backgrounds.length - 1) {
|
if (this.backgroundIndex > this.backgrounds.length - 1) {
|
||||||
this.backgroundIndex = 0;
|
this.backgroundIndex = 0;
|
||||||
}
|
}
|
||||||
} else if ('string' === typeof indexOrName) {
|
} else if ("string" === typeof indexOrName) {
|
||||||
this.backgroundIndex = this.backgrounds.findIndex(({ name }) => name === indexOrName);
|
this.backgroundIndex = this.backgrounds.findIndex(
|
||||||
|
({ name }) => name === indexOrName,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
this.backgroundIndex = indexOrName;
|
this.backgroundIndex = indexOrName;
|
||||||
}
|
}
|
||||||
const { name, backgroundColor, textColor } = this.backgrounds[this.backgroundIndex];
|
const { name, backgroundColor, textColor } =
|
||||||
|
this.backgrounds[this.backgroundIndex];
|
||||||
|
|
||||||
this.$body.style.setProperty('background', backgroundColor);
|
this.$body.style.setProperty("background", backgroundColor);
|
||||||
this.$body.style.setProperty('color', textColor);
|
this.$body.style.setProperty("color", textColor);
|
||||||
|
|
||||||
// update current settings
|
// update current settings
|
||||||
this.updateUrlParams({ background: name });
|
this.updateUrlParams({ background: name });
|
||||||
@ -1013,15 +1062,15 @@ class Gamepad {
|
|||||||
// ensure that a gamepad is currently active
|
// ensure that a gamepad is currently active
|
||||||
if (this.index === null) return;
|
if (this.index === null) return;
|
||||||
|
|
||||||
if ('undefined' === typeof color) {
|
if ("undefined" === typeof color) {
|
||||||
// no color was specified, load the next one in list
|
// no color was specified, load the next one in list
|
||||||
this.colorIndex++;
|
this.colorIndex++;
|
||||||
if (this.colorIndex > this.identifier.colors.length - 1) {
|
if (this.colorIndex > this.identifier.colors.length - 1) {
|
||||||
this.colorIndex = 0;
|
this.colorIndex = 0;
|
||||||
}
|
}
|
||||||
} else if ('string' === typeof style) {
|
} else if ("string" === typeof style) {
|
||||||
this.colorIndex = this.identifier.colors.findIndex(
|
this.colorIndex = this.identifier.colors.findIndex(
|
||||||
(c) => c === color
|
(c) => c === color,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
if (!Number.isNaN(Number.parseInt(color))) {
|
if (!Number.isNaN(Number.parseInt(color))) {
|
||||||
@ -1043,7 +1092,7 @@ class Gamepad {
|
|||||||
: null;
|
: null;
|
||||||
|
|
||||||
// update the DOM with the color value
|
// update the DOM with the color value
|
||||||
this.$gamepad.setAttribute('data-color', this.colorName);
|
this.$gamepad.setAttribute("data-color", this.colorName);
|
||||||
|
|
||||||
// update current settings
|
// update current settings
|
||||||
this.updateUrlParams({ color: this.colorName });
|
this.updateUrlParams({ color: this.colorName });
|
||||||
@ -1060,25 +1109,25 @@ class Gamepad {
|
|||||||
if (this.index === null) return;
|
if (this.index === null) return;
|
||||||
|
|
||||||
// ensure we have some data to process
|
// ensure we have some data to process
|
||||||
if (typeof level === 'undefined') return;
|
if (typeof level === "undefined") return;
|
||||||
|
|
||||||
this.zoomMode = level === 'auto' ? 'auto' : 'manual';
|
this.zoomMode = level === "auto" ? "auto" : "manual";
|
||||||
|
|
||||||
if (this.zoomMode === 'auto') {
|
if (this.zoomMode === "auto") {
|
||||||
// 'auto' means a 'contained in window' zoom, with a max zoom of 1
|
// 'auto' means a 'contained in window' zoom, with a max zoom of 1
|
||||||
const { width, height } = this.$gamepad.getBoundingClientRect();
|
const { width, height } = this.$gamepad.getBoundingClientRect();
|
||||||
this.zoomLevel = Math.min(
|
this.zoomLevel = Math.min(
|
||||||
window.innerWidth / width,
|
window.innerWidth / width,
|
||||||
window.innerHeight / height,
|
window.innerHeight / height,
|
||||||
1
|
1,
|
||||||
);
|
);
|
||||||
} else if (level === 0) {
|
} else if (level === 0) {
|
||||||
// 0 means a zoom reset
|
// 0 means a zoom reset
|
||||||
this.zoomLevel = 1;
|
this.zoomLevel = 1;
|
||||||
} else if (level === '+' && this.zoomLevel < 4) {
|
} else if (level === "+" && this.zoomLevel < 4) {
|
||||||
// '+' means a zoom in if we still can
|
// '+' means a zoom in if we still can
|
||||||
this.zoomLevel += 0.1;
|
this.zoomLevel += 0.1;
|
||||||
} else if (level === '-' && this.zoomLevel > 0.1) {
|
} else if (level === "-" && this.zoomLevel > 0.1) {
|
||||||
// '-' means a zoom out if we still can
|
// '-' means a zoom out if we still can
|
||||||
this.zoomLevel -= 0.1;
|
this.zoomLevel -= 0.1;
|
||||||
} else if (!Number.isNaN(+level)) {
|
} else if (!Number.isNaN(+level)) {
|
||||||
@ -1091,13 +1140,13 @@ class Gamepad {
|
|||||||
|
|
||||||
// update the DOM with the zoom value
|
// update the DOM with the zoom value
|
||||||
this.$gamepad.style.setProperty(
|
this.$gamepad.style.setProperty(
|
||||||
'transform',
|
"transform",
|
||||||
`scale(${this.zoomLevel}, ${this.zoomLevel})`
|
`scale(${this.zoomLevel}, ${this.zoomLevel})`,
|
||||||
);
|
);
|
||||||
|
|
||||||
// update current settings
|
// update current settings
|
||||||
this.updateUrlParams({
|
this.updateUrlParams({
|
||||||
zoom: this.zoomMode === 'auto' ? undefined : this.zoomLevel,
|
zoom: this.zoomMode === "auto" ? undefined : this.zoomLevel,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1113,7 +1162,7 @@ class Gamepad {
|
|||||||
|
|
||||||
// compute next type
|
// compute next type
|
||||||
const types = Object.keys(this.identifiers).filter(
|
const types = Object.keys(this.identifiers).filter(
|
||||||
(i) => i !== 'debug'
|
(i) => i !== "debug",
|
||||||
);
|
);
|
||||||
let typeIndex = types.reduce((typeIndex, type, index) => {
|
let typeIndex = types.reduce((typeIndex, type, index) => {
|
||||||
return type === this.type ? index : typeIndex;
|
return type === this.type ? index : typeIndex;
|
||||||
@ -1140,7 +1189,7 @@ class Gamepad {
|
|||||||
this.debug = debug !== undefined ? debug : !this.debug;
|
this.debug = debug !== undefined ? debug : !this.debug;
|
||||||
|
|
||||||
// update current settings
|
// update current settings
|
||||||
this.changeSkin(this.debug ? 'debug' : 'auto')
|
this.changeSkin(this.debug ? "debug" : "auto");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1148,8 +1197,8 @@ class Gamepad {
|
|||||||
*/
|
*/
|
||||||
toggleHelp() {
|
toggleHelp() {
|
||||||
// display the help popout
|
// display the help popout
|
||||||
this.$helpPopout.classList.toggle('active');
|
this.$helpPopout.classList.toggle("active");
|
||||||
this.helpVisible = this.$helpPopout.classList.contains('active');
|
this.helpVisible = this.$helpPopout.classList.contains("active");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1162,9 +1211,12 @@ class Gamepad {
|
|||||||
if (this.index === null) return;
|
if (this.index === null) return;
|
||||||
|
|
||||||
// update current settings
|
// update current settings
|
||||||
this.useMeterTriggers = useMeter !== undefined ? useMeter : !this.useMeterTriggers;
|
this.useMeterTriggers =
|
||||||
this.$gamepad.classList[this.useMeterTriggers ? 'add' : 'remove']('triggers-meter');
|
useMeter !== undefined ? useMeter : !this.useMeterTriggers;
|
||||||
const triggers = this.useMeterTriggers ? 'meter' : 'opacity';
|
this.$gamepad.classList[this.useMeterTriggers ? "add" : "remove"](
|
||||||
|
"triggers-meter",
|
||||||
|
);
|
||||||
|
const triggers = this.useMeterTriggers ? "meter" : "opacity";
|
||||||
this.updateUrlParams({ triggers });
|
this.updateUrlParams({ triggers });
|
||||||
this.$triggersSelect.value = triggers;
|
this.$triggersSelect.value = triggers;
|
||||||
}
|
}
|
||||||
@ -1177,7 +1229,7 @@ class Gamepad {
|
|||||||
*/
|
*/
|
||||||
getUrlParam(name) {
|
getUrlParam(name) {
|
||||||
const matches = new RegExp(`[?&]${name}(=([^&#]*))?`).exec(
|
const matches = new RegExp(`[?&]${name}(=([^&#]*))?`).exec(
|
||||||
window.location.search
|
window.location.search,
|
||||||
);
|
);
|
||||||
return matches ? decodeURIComponent(matches[2] || true) || true : null;
|
return matches ? decodeURIComponent(matches[2] || true) || true : null;
|
||||||
}
|
}
|
||||||
@ -1189,14 +1241,14 @@ class Gamepad {
|
|||||||
*/
|
*/
|
||||||
getUrlParams() {
|
getUrlParams() {
|
||||||
const settingsArr = window.location.search
|
const settingsArr = window.location.search
|
||||||
.replace('?', '')
|
.replace("?", "")
|
||||||
.split('&')
|
.split("&")
|
||||||
.map((param) => param.split('='));
|
.map((param) => param.split("="));
|
||||||
const settings = {};
|
const settings = {};
|
||||||
for (const key of Object.keys(settingsArr)) {
|
for (const key of Object.keys(settingsArr)) {
|
||||||
const [k, v] = settingsArr[key];
|
const [k, v] = settingsArr[key];
|
||||||
settings[k] = v;
|
settings[k] = v;
|
||||||
};
|
}
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1224,7 +1276,7 @@ class Gamepad {
|
|||||||
const query = Object.entries(params)
|
const query = Object.entries(params)
|
||||||
.filter(([, value]) => value !== undefined && value !== null)
|
.filter(([, value]) => value !== undefined && value !== null)
|
||||||
.map(([key, value]) => `${key}=${value}`)
|
.map(([key, value]) => `${key}=${value}`)
|
||||||
.join('&');
|
.join("&");
|
||||||
window.history.replaceState({}, document.title, `/?${query}`);
|
window.history.replaceState({}, document.title, `/?${query}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
1
obs-commandline.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
--disable-features=EnableWindowsGamingInputDataFetcher
|
@ -5,8 +5,10 @@ window.gamepad.TemplateClass = class DebugTemplate {
|
|||||||
constructor() {
|
constructor() {
|
||||||
this.gamepad = window.gamepad;
|
this.gamepad = window.gamepad;
|
||||||
this.init();
|
this.init();
|
||||||
this.gamepad.updateButton = ($button, { value }) => this.updateElem($button, value);
|
this.gamepad.updateButton = ($button, { value }) =>
|
||||||
this.gamepad.updateAxis = ($axis, _, axis) => this.updateElem($axis, axis, 6);
|
this.updateElem($button, value);
|
||||||
|
this.gamepad.updateAxis = ($axis, _, axis) =>
|
||||||
|
this.updateElem($axis, axis, 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -21,35 +23,37 @@ window.gamepad.TemplateClass = class DebugTemplate {
|
|||||||
* Initializes the template
|
* Initializes the template
|
||||||
*/
|
*/
|
||||||
init() {
|
init() {
|
||||||
this.$name = document.querySelector('#info-name .value');
|
this.$name = document.querySelector("#info-name .value");
|
||||||
this.$vendor = document.querySelector('#info-vendor');
|
this.$vendor = document.querySelector("#info-vendor");
|
||||||
this.$product = document.querySelector('#info-product');
|
this.$product = document.querySelector("#info-product");
|
||||||
this.$id = document.querySelector('#info-id');
|
this.$id = document.querySelector("#info-id");
|
||||||
this.$timestamp = document.querySelector('#info-timestamp .value');
|
this.$timestamp = document.querySelector("#info-timestamp .value");
|
||||||
this.$index = document.querySelector('#info-index .value');
|
this.$index = document.querySelector("#info-index .value");
|
||||||
this.$mapping = document.querySelector('#info-mapping .value');
|
this.$mapping = document.querySelector("#info-mapping .value");
|
||||||
this.$rumble = document.querySelector('#info-rumble .value');
|
this.$rumble = document.querySelector("#info-rumble .value");
|
||||||
this.$axes = document.querySelector('.axes .container');
|
this.$axes = document.querySelector(".axes .container");
|
||||||
this.$buttons = document.querySelector('.buttons .container');
|
this.$buttons = document.querySelector(".buttons .container");
|
||||||
const activeGamepad = this.gamepad.getActive();
|
const activeGamepad = this.gamepad.getActive();
|
||||||
const { name, vendor, product, id } = this.gamepad.toGamepadInfo(activeGamepad.id);
|
const { name, vendor, product, id } = this.gamepad.toGamepadInfo(
|
||||||
|
activeGamepad.id,
|
||||||
|
);
|
||||||
this.$name.innerHTML = name;
|
this.$name.innerHTML = name;
|
||||||
this.$name.setAttribute('title', activeGamepad.id);
|
this.$name.setAttribute("title", activeGamepad.id);
|
||||||
if (vendor && product) {
|
if (vendor && product) {
|
||||||
this.$vendor.querySelector('.value').innerHTML = vendor;
|
this.$vendor.querySelector(".value").innerHTML = vendor;
|
||||||
this.$product.querySelector('.value').innerHTML = product;
|
this.$product.querySelector(".value").innerHTML = product;
|
||||||
this.$vendor.style.setProperty('display', 'block');
|
this.$vendor.style.setProperty("display", "block");
|
||||||
this.$product.style.setProperty('display', 'block');
|
this.$product.style.setProperty("display", "block");
|
||||||
} else {
|
} else {
|
||||||
this.$id.querySelector('.value').innerHTML = id;
|
this.$id.querySelector(".value").innerHTML = id;
|
||||||
this.$id.style.setProperty('display', 'block');
|
this.$id.style.setProperty("display", "block");
|
||||||
}
|
}
|
||||||
this.updateTimestamp();
|
this.updateTimestamp();
|
||||||
this.$index.innerHTML = this.activeGamepad.index;
|
this.$index.innerHTML = this.activeGamepad.index;
|
||||||
this.$mapping.innerHTML = this.activeGamepad.mapping || 'N/A';
|
this.$mapping.innerHTML = this.activeGamepad.mapping || "N/A";
|
||||||
this.$rumble.innerHTML = this.activeGamepad.vibrationActuator
|
this.$rumble.innerHTML = this.activeGamepad.vibrationActuator
|
||||||
? this.activeGamepad.vibrationActuator.type
|
? this.activeGamepad.vibrationActuator.type
|
||||||
: 'N/A';
|
: "N/A";
|
||||||
this.initAxes();
|
this.initAxes();
|
||||||
this.initButtons();
|
this.initButtons();
|
||||||
}
|
}
|
||||||
@ -75,8 +79,8 @@ window.gamepad.TemplateClass = class DebugTemplate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the buttons
|
* Initializes the buttons
|
||||||
*/
|
*/
|
||||||
initButtons() {
|
initButtons() {
|
||||||
for (
|
for (
|
||||||
let buttonIndex = 0;
|
let buttonIndex = 0;
|
||||||
@ -104,7 +108,7 @@ window.gamepad.TemplateClass = class DebugTemplate {
|
|||||||
this.updateTimestamp();
|
this.updateTimestamp();
|
||||||
$elem.innerHTML = value.toFixed(precision);
|
$elem.innerHTML = value.toFixed(precision);
|
||||||
const color = Math.floor(255 * 0.3 + 255 * 0.7 * Math.abs(value));
|
const color = Math.floor(255 * 0.3 + 255 * 0.7 * Math.abs(value));
|
||||||
$elem.style.setProperty('color', `rgb(${color}, ${color}, ${color})`);
|
$elem.style.setProperty("color", `rgb(${color}, ${color}, ${color})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -115,6 +119,8 @@ window.gamepad.TemplateClass = class DebugTemplate {
|
|||||||
if (!this.activeGamepad) {
|
if (!this.activeGamepad) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.$timestamp.innerHTML = Number.parseFloat(this.activeGamepad.timestamp).toFixed(3);
|
this.$timestamp.innerHTML = Number.parseFloat(
|
||||||
|
this.activeGamepad.timestamp,
|
||||||
|
).toFixed(3);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -23,8 +23,18 @@
|
|||||||
<span class="button y" data-button="3"></span>
|
<span class="button y" data-button="3"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="sticks">
|
<div class="sticks">
|
||||||
<span class="stick left" data-button="10" data-axis-x="0" data-axis-y="1"></span>
|
<span
|
||||||
<span class="stick right" data-button="11" data-axis-x="2" data-axis-y="3"></span>
|
class="stick left"
|
||||||
|
data-button="10"
|
||||||
|
data-axis-x="0"
|
||||||
|
data-axis-y="1"
|
||||||
|
></span>
|
||||||
|
<span
|
||||||
|
class="stick right"
|
||||||
|
data-button="11"
|
||||||
|
data-axis-x="2"
|
||||||
|
data-axis-y="3"
|
||||||
|
></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="dpad">
|
<div class="dpad">
|
||||||
<span class="face up" data-button="12"></span>
|
<span class="face up" data-button="12"></span>
|
||||||
|
@ -19,26 +19,41 @@ window.gamepad.TemplateClass = class DualShock4Template {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateButton($button, button) {
|
updateButton($button, button) {
|
||||||
if (!$button.matches('.trigger') || !button) return;
|
if (!$button.matches(".trigger") || !button) return;
|
||||||
$button.style.setProperty('opacity', this.gamepad.useMeterTriggers ? 1 : `${button.value * 100}%`);
|
$button.style.setProperty(
|
||||||
$button.style.setProperty('clip-path', this.gamepad.useMeterTriggers ? `inset(${100 - button.value * 100}% 0px 0px 0pc)` : 'none');
|
"opacity",
|
||||||
|
this.gamepad.useMeterTriggers ? 1 : `${button.value * 100}%`,
|
||||||
|
);
|
||||||
|
$button.style.setProperty(
|
||||||
|
"clip-path",
|
||||||
|
this.gamepad.useMeterTriggers
|
||||||
|
? `inset(${100 - button.value * 100}% 0px 0px 0pc)`
|
||||||
|
: "none",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateAxis($axis, attribute, axis) {
|
updateAxis($axis, attribute, axis) {
|
||||||
if (!$axis.matches('.stick')) return;
|
if (!$axis.matches(".stick")) return;
|
||||||
if (attribute === 'data-axis-x') {
|
if (attribute === "data-axis-x") {
|
||||||
$axis.style.setProperty('margin-left', `${axis * 25}px`);
|
$axis.style.setProperty("margin-left", `${axis * 25}px`);
|
||||||
this.updateRotate($axis);
|
this.updateRotate($axis);
|
||||||
}
|
}
|
||||||
if (attribute === 'data-axis-y') {
|
if (attribute === "data-axis-y") {
|
||||||
$axis.style.setProperty('margin-top', `${axis * 25}px`);
|
$axis.style.setProperty("margin-top", `${axis * 25}px`);
|
||||||
this.updateRotate($axis);
|
this.updateRotate($axis);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateRotate($axis) {
|
updateRotate($axis) {
|
||||||
const rotateX = Number.parseFloat($axis.getAttribute('data-value-y') * 30);
|
const rotateX = Number.parseFloat(
|
||||||
const rotateY = -Number.parseFloat($axis.getAttribute('data-value-x') * 30);
|
$axis.getAttribute("data-value-y") * 30,
|
||||||
$axis.style.setProperty('transform', `rotateX(${rotateX}deg) rotateY(${rotateY}deg)`);
|
);
|
||||||
|
const rotateY = -Number.parseFloat(
|
||||||
|
$axis.getAttribute("data-value-x") * 30,
|
||||||
|
);
|
||||||
|
$axis.style.setProperty(
|
||||||
|
"transform",
|
||||||
|
`rotateX(${rotateX}deg) rotateY(${rotateY}deg)`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -23,8 +23,18 @@
|
|||||||
<span class="button y" data-button="3"></span>
|
<span class="button y" data-button="3"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="sticks">
|
<div class="sticks">
|
||||||
<span class="stick left" data-button="10" data-axis-x="0" data-axis-y="1"></span>
|
<span
|
||||||
<span class="stick right" data-button="11" data-axis-x="2" data-axis-y="3"></span>
|
class="stick left"
|
||||||
|
data-button="10"
|
||||||
|
data-axis-x="0"
|
||||||
|
data-axis-y="1"
|
||||||
|
></span>
|
||||||
|
<span
|
||||||
|
class="stick right"
|
||||||
|
data-button="11"
|
||||||
|
data-axis-x="2"
|
||||||
|
data-axis-y="3"
|
||||||
|
></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="dpad">
|
<div class="dpad">
|
||||||
<span class="face up" data-button="12"></span>
|
<span class="face up" data-button="12"></span>
|
||||||
|
@ -19,26 +19,41 @@ window.gamepad.TemplateClass = class DualSenseTemplate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateButton($button, button) {
|
updateButton($button, button) {
|
||||||
if (!$button.matches('.trigger') || !button) return;
|
if (!$button.matches(".trigger") || !button) return;
|
||||||
$button.style.setProperty('opacity', this.gamepad.useMeterTriggers ? 1 : `${button.value * 100}%`);
|
$button.style.setProperty(
|
||||||
$button.style.setProperty('clip-path', this.gamepad.useMeterTriggers ? `inset(${100 - button.value * 100}% 0px 0px 0pc)` : 'none');
|
"opacity",
|
||||||
|
this.gamepad.useMeterTriggers ? 1 : `${button.value * 100}%`,
|
||||||
|
);
|
||||||
|
$button.style.setProperty(
|
||||||
|
"clip-path",
|
||||||
|
this.gamepad.useMeterTriggers
|
||||||
|
? `inset(${100 - button.value * 100}% 0px 0px 0pc)`
|
||||||
|
: "none",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateAxis($axis, attribute, axis) {
|
updateAxis($axis, attribute, axis) {
|
||||||
if (!$axis.matches('.stick')) return;
|
if (!$axis.matches(".stick")) return;
|
||||||
if (attribute === 'data-axis-x') {
|
if (attribute === "data-axis-x") {
|
||||||
$axis.style.setProperty('margin-left', `${axis * 25}px`);
|
$axis.style.setProperty("margin-left", `${axis * 25}px`);
|
||||||
this.updateRotate($axis);
|
this.updateRotate($axis);
|
||||||
}
|
}
|
||||||
if (attribute === 'data-axis-y') {
|
if (attribute === "data-axis-y") {
|
||||||
$axis.style.setProperty('margin-top', `${axis * 25}px`);
|
$axis.style.setProperty("margin-top", `${axis * 25}px`);
|
||||||
this.updateRotate($axis);
|
this.updateRotate($axis);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateRotate($axis) {
|
updateRotate($axis) {
|
||||||
const rotateX = Number.parseFloat($axis.getAttribute('data-value-y') * 30);
|
const rotateX = Number.parseFloat(
|
||||||
const rotateY = -Number.parseFloat($axis.getAttribute('data-value-x') * 30);
|
$axis.getAttribute("data-value-y") * 30,
|
||||||
$axis.style.setProperty('transform', `rotateX(${rotateX}deg) rotateY(${rotateY}deg)`);
|
);
|
||||||
|
const rotateY = -Number.parseFloat(
|
||||||
|
$axis.getAttribute("data-value-x") * 30,
|
||||||
|
);
|
||||||
|
$axis.style.setProperty(
|
||||||
|
"transform",
|
||||||
|
`rotateX(${rotateX}deg) rotateY(${rotateY}deg)`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -11,9 +11,9 @@
|
|||||||
--white-color: white;
|
--white-color: white;
|
||||||
--main-bg-color: black;
|
--main-bg-color: black;
|
||||||
--main-component-color: #333;
|
--main-component-color: #333;
|
||||||
--clutch-color: #2D64B9;
|
--clutch-color: #2d64b9;
|
||||||
--brake-color: #A52725;
|
--brake-color: #a52725;
|
||||||
--throttle-color: #0CA818;
|
--throttle-color: #0ca818;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad #telemetry:has(#steering:not([style*="display: none"])) {
|
#gamepad #telemetry:has(#steering:not([style*="display: none"])) {
|
||||||
@ -26,7 +26,7 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad #telemetry>*+* {
|
#gamepad #telemetry > * + * {
|
||||||
margin-left: 2px;
|
margin-left: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +53,7 @@
|
|||||||
width: 30px;
|
width: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gamepad #telemetry #meters .meter+.meter {
|
#gamepad #telemetry #meters .meter + .meter {
|
||||||
margin-left: 2px;
|
margin-left: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,7 +133,6 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#wizard .wizard-options {
|
#wizard .wizard-options {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
|
@ -3,7 +3,7 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
|
|||||||
* Instanciates a new telemetry template
|
* Instanciates a new telemetry template
|
||||||
*/
|
*/
|
||||||
constructor() {
|
constructor() {
|
||||||
this.AXES = ['clutch', 'brake', 'throttle', 'steering'];
|
this.AXES = ["clutch", "brake", "throttle", "steering"];
|
||||||
this.gamepad = window.gamepad;
|
this.gamepad = window.gamepad;
|
||||||
|
|
||||||
this.loadSelectors();
|
this.loadSelectors();
|
||||||
@ -33,7 +33,10 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
|
|||||||
* @returns {number}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
toPercentage(value, min, max) {
|
toPercentage(value, min, max) {
|
||||||
return Math.max(0, Math.min(100, Math.round((value - min) * (100 / (max - min)))));
|
return Math.max(
|
||||||
|
0,
|
||||||
|
Math.min(100, Math.round((value - min) * (100 / (max - min)))),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -46,7 +49,7 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
|
|||||||
*/
|
*/
|
||||||
toDegrees(value, min, max) {
|
toDegrees(value, min, max) {
|
||||||
const percentage = this.toPercentage(value, min, max);
|
const percentage = this.toPercentage(value, min, max);
|
||||||
return (this.angle) * (percentage - 50) / 100;
|
return (this.angle * (percentage - 50)) / 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -59,10 +62,11 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
|
|||||||
toAxisValue(gamepad, axis) {
|
toAxisValue(gamepad, axis) {
|
||||||
const { type, index, min, max } = this[axis];
|
const { type, index, min, max } = this[axis];
|
||||||
if (!type || !index) return null;
|
if (!type || !index) return null;
|
||||||
const value = type === 'button'
|
const value =
|
||||||
? gamepad.buttons[index].value
|
type === "button"
|
||||||
: gamepad.axes[index];
|
? gamepad.buttons[index].value
|
||||||
return axis === 'steering'
|
: gamepad.axes[index];
|
||||||
|
return axis === "steering"
|
||||||
? this.toDegrees(value, min, max)
|
? this.toDegrees(value, min, max)
|
||||||
: this.toPercentage(value, min, max);
|
: this.toPercentage(value, min, max);
|
||||||
}
|
}
|
||||||
@ -71,49 +75,53 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
|
|||||||
* Loads the DOM selectors
|
* Loads the DOM selectors
|
||||||
*/
|
*/
|
||||||
loadSelectors() {
|
loadSelectors() {
|
||||||
this.$telemetry = document.querySelector('#telemetry');
|
this.$telemetry = document.querySelector("#telemetry");
|
||||||
this.$chart = this.$telemetry.querySelector('#chart');
|
this.$chart = this.$telemetry.querySelector("#chart");
|
||||||
this.chartContext = this.$chart.getContext('2d');
|
this.chartContext = this.$chart.getContext("2d");
|
||||||
this.$meters = this.$telemetry.querySelector('#meters');
|
this.$meters = this.$telemetry.querySelector("#meters");
|
||||||
this.$clutch = this.$telemetry.querySelector('#clutch');
|
this.$clutch = this.$telemetry.querySelector("#clutch");
|
||||||
this.$clutchBar = this.$clutch.querySelector('.bar');
|
this.$clutchBar = this.$clutch.querySelector(".bar");
|
||||||
this.$clutchValue = this.$clutch.querySelector('.value');
|
this.$clutchValue = this.$clutch.querySelector(".value");
|
||||||
this.$brake = this.$telemetry.querySelector('#brake');
|
this.$brake = this.$telemetry.querySelector("#brake");
|
||||||
this.$brakeBar = this.$brake.querySelector('.bar');
|
this.$brakeBar = this.$brake.querySelector(".bar");
|
||||||
this.$brakeValue = this.$brake.querySelector('.value');
|
this.$brakeValue = this.$brake.querySelector(".value");
|
||||||
this.$throttle = this.$telemetry.querySelector('#throttle');
|
this.$throttle = this.$telemetry.querySelector("#throttle");
|
||||||
this.$throttleBar = this.$throttle.querySelector('.bar');
|
this.$throttleBar = this.$throttle.querySelector(".bar");
|
||||||
this.$throttleValue = this.$throttle.querySelector('.value');
|
this.$throttleValue = this.$throttle.querySelector(".value");
|
||||||
this.$steering = this.$telemetry.querySelector('#steering');
|
this.$steering = this.$telemetry.querySelector("#steering");
|
||||||
this.$steeringIndicator = this.$steering.querySelector('.indicator');
|
this.$steeringIndicator = this.$steering.querySelector(".indicator");
|
||||||
this.$wizard = document.querySelector('#wizard');
|
this.$wizard = document.querySelector("#wizard");
|
||||||
this.$wizardPreview = this.$wizard.querySelector('#wizard-preview');
|
this.$wizardPreview = this.$wizard.querySelector("#wizard-preview");
|
||||||
this.$wizardInstructions = this.$wizard.querySelector('#wizard-instructions');
|
this.$wizardInstructions = this.$wizard.querySelector(
|
||||||
|
"#wizard-instructions",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the params from the URL
|
* Loads the params from the URL
|
||||||
*/
|
*/
|
||||||
loadParams() {
|
loadParams() {
|
||||||
this.withChart = gamepad.getUrlParam('chart') !== 'false';
|
this.withChart = gamepad.getUrlParam("chart") !== "false";
|
||||||
this.chartColors = {
|
this.chartColors = {
|
||||||
clutch: '#2D64B9',
|
clutch: "#2D64B9",
|
||||||
brake: '#A52725',
|
brake: "#A52725",
|
||||||
throttle: '#0CA818',
|
throttle: "#0CA818",
|
||||||
}
|
};
|
||||||
this.historyLength = +(gamepad.getUrlParam('history') || 5000);
|
this.historyLength = +(gamepad.getUrlParam("history") || 5000);
|
||||||
this.withMeters = gamepad.getUrlParam('meters') !== 'false';
|
this.withMeters = gamepad.getUrlParam("meters") !== "false";
|
||||||
this.withSteering = gamepad.getUrlParam('steeringIndex') !== null;
|
this.withSteering = gamepad.getUrlParam("steeringIndex") !== null;
|
||||||
this.angle = gamepad.getUrlParam('angle') || 360;
|
this.angle = gamepad.getUrlParam("angle") || 360;
|
||||||
this.frequency = gamepad.getUrlParam('fps') || 60;
|
this.frequency = gamepad.getUrlParam("fps") || 60;
|
||||||
for(const axis of this.AXES) {
|
for (const axis of this.AXES) {
|
||||||
this[axis] = {
|
this[axis] = {
|
||||||
type: (gamepad.getUrlParam(`${axis}Type`) || 'axis').replace('axis', 'axe'),
|
type: (gamepad.getUrlParam(`${axis}Type`) || "axis").replace(
|
||||||
|
"axis",
|
||||||
|
"axe",
|
||||||
|
),
|
||||||
index: gamepad.getUrlParam(`${axis}Index`),
|
index: gamepad.getUrlParam(`${axis}Index`),
|
||||||
min: gamepad.getUrlParam(`${axis}Min`) || -1,
|
min: gamepad.getUrlParam(`${axis}Min`) || -1,
|
||||||
max: gamepad.getUrlParam(`${axis}Max`) || 1,
|
max: gamepad.getUrlParam(`${axis}Max`) || 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,7 +139,7 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
|
|||||||
if (!this.steering.index) {
|
if (!this.steering.index) {
|
||||||
this.$steering.remove();
|
this.$steering.remove();
|
||||||
} else {
|
} else {
|
||||||
this.$telemetry.classList.add('with-steering');
|
this.$telemetry.classList.add("with-steering");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,9 +168,16 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
|
|||||||
this.scaleChart();
|
this.scaleChart();
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
this.chartData = [];
|
this.chartData = [];
|
||||||
for (let index = 0; index < this.historyLength / this.interval; index++) {
|
for (
|
||||||
const timestamp = now - (this.historyLength - index * this.interval);
|
let index = 0;
|
||||||
const data = demo ? this.getDemoData() : { clutch: 0, brake: 0, throttle: 0 };
|
index < this.historyLength / this.interval;
|
||||||
|
index++
|
||||||
|
) {
|
||||||
|
const timestamp =
|
||||||
|
now - (this.historyLength - index * this.interval);
|
||||||
|
const data = demo
|
||||||
|
? this.getDemoData()
|
||||||
|
: { clutch: 0, brake: 0, throttle: 0 };
|
||||||
this.chartData.push({ timestamp, ...data });
|
this.chartData.push({ timestamp, ...data });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -187,7 +202,9 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
|
|||||||
if (!this.running) return;
|
if (!this.running) return;
|
||||||
|
|
||||||
const gamepad = this.gamepad.getActive();
|
const gamepad = this.gamepad.getActive();
|
||||||
const [clutch, brake, throttle, steering] = this.AXES.map((axis) => this.toAxisValue(gamepad, axis));
|
const [clutch, brake, throttle, steering] = this.AXES.map((axis) =>
|
||||||
|
this.toAxisValue(gamepad, axis),
|
||||||
|
);
|
||||||
if (this.withChart) this.drawChart(clutch, brake, throttle);
|
if (this.withChart) this.drawChart(clutch, brake, throttle);
|
||||||
if (this.withMeters) this.updateMeters(clutch, brake, throttle);
|
if (this.withMeters) this.updateMeters(clutch, brake, throttle);
|
||||||
if (this.withSteering) this.updateSteering(steering);
|
if (this.withSteering) this.updateSteering(steering);
|
||||||
@ -202,10 +219,38 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
|
|||||||
* @returns {object}
|
* @returns {object}
|
||||||
*/
|
*/
|
||||||
getDemoData() {
|
getDemoData() {
|
||||||
const clutch = this.withClutch ? Math.max(0, Math.round(Math.sin(this.demoIndex / (1000 / this.interval)) * 100)) : 0;
|
const clutch = this.withClutch
|
||||||
const brake = this.withBrake ? Math.max(0, Math.round(Math.sin(this.demoIndex / (1000 / this.interval) + 1.5) * 100)) : 0;
|
? Math.max(
|
||||||
const throttle = this.withThrottle ? Math.max(0, Math.round(Math.sin(this.demoIndex / (1000 / this.interval) + 3) * 100)) : 0;
|
0,
|
||||||
const steering = this.withSteering ? Math.round(Math.sin(this.demoIndex / (1000 / this.interval) + 3) * this.angle) : 0;
|
Math.round(
|
||||||
|
Math.sin(this.demoIndex / (1000 / this.interval)) * 100,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: 0;
|
||||||
|
const brake = this.withBrake
|
||||||
|
? Math.max(
|
||||||
|
0,
|
||||||
|
Math.round(
|
||||||
|
Math.sin(this.demoIndex / (1000 / this.interval) + 1.5) *
|
||||||
|
100,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: 0;
|
||||||
|
const throttle = this.withThrottle
|
||||||
|
? Math.max(
|
||||||
|
0,
|
||||||
|
Math.round(
|
||||||
|
Math.sin(this.demoIndex / (1000 / this.interval) + 3) *
|
||||||
|
100,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: 0;
|
||||||
|
const steering = this.withSteering
|
||||||
|
? Math.round(
|
||||||
|
Math.sin(this.demoIndex / (1000 / this.interval) + 3) *
|
||||||
|
this.angle,
|
||||||
|
)
|
||||||
|
: 0;
|
||||||
if (this.demoIndex++ > 10000) this.demoIndex = 0;
|
if (this.demoIndex++ > 10000) this.demoIndex = 0;
|
||||||
return { clutch, brake, throttle, steering };
|
return { clutch, brake, throttle, steering };
|
||||||
}
|
}
|
||||||
@ -256,14 +301,17 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
|
|||||||
chartContext.clearRect(0, 0, width, height);
|
chartContext.clearRect(0, 0, width, height);
|
||||||
|
|
||||||
for (const axis of this.AXES) {
|
for (const axis of this.AXES) {
|
||||||
if (axis === 'steering') continue;
|
if (axis === "steering") continue;
|
||||||
chartContext.beginPath();
|
chartContext.beginPath();
|
||||||
|
|
||||||
for (let index = 0; index < this.chartData.length; index++) {
|
for (let index = 0; index < this.chartData.length; index++) {
|
||||||
const entry = this.chartData[index];
|
const entry = this.chartData[index];
|
||||||
const x = (entry.timestamp + this.historyLength - now) / this.historyLength * width;
|
const x =
|
||||||
const y = (101 - (entry[axis] || 0)) * height / 100;
|
((entry.timestamp + this.historyLength - now) /
|
||||||
chartContext[index === 0 ? 'moveTo' : 'lineTo'](x, y);
|
this.historyLength) *
|
||||||
|
width;
|
||||||
|
const y = ((101 - (entry[axis] || 0)) * height) / 100;
|
||||||
|
chartContext[index === 0 ? "moveTo" : "lineTo"](x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
chartContext.strokeStyle = this.chartColors[axis];
|
chartContext.strokeStyle = this.chartColors[axis];
|
||||||
@ -280,10 +328,15 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
|
|||||||
* @param {number} steering
|
* @param {number} steering
|
||||||
*/
|
*/
|
||||||
updateMeters(clutch, brake, throttle) {
|
updateMeters(clutch, brake, throttle) {
|
||||||
for (const [axis, value] of Object.entries({ clutch, brake, throttle })) {
|
for (const [axis, value] of Object.entries({
|
||||||
|
clutch,
|
||||||
|
brake,
|
||||||
|
throttle,
|
||||||
|
})) {
|
||||||
if (value === null) continue;
|
if (value === null) continue;
|
||||||
this[`$${axis}Value`].innerHTML = value;
|
this[`$${axis}Value`].innerHTML = value;
|
||||||
this[`$${axis}Value`].style.opacity = `${Math.round(33 + (value / 1.5))}%`;
|
this[`$${axis}Value`].style.opacity =
|
||||||
|
`${Math.round(33 + value / 1.5)}%`;
|
||||||
this[`$${axis}Bar`].style.height = `${value}%`;
|
this[`$${axis}Bar`].style.height = `${value}%`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -302,7 +355,9 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
|
|||||||
*/
|
*/
|
||||||
setupWizard() {
|
setupWizard() {
|
||||||
this.interval = 1000 / 60;
|
this.interval = 1000 / 60;
|
||||||
this.$wizardInstructions.querySelector('#wizard-preview').appendChild(this.$telemetry);
|
this.$wizardInstructions
|
||||||
|
.querySelector("#wizard-preview")
|
||||||
|
.appendChild(this.$telemetry);
|
||||||
|
|
||||||
this.withChart = true;
|
this.withChart = true;
|
||||||
this.withMeters = true;
|
this.withMeters = true;
|
||||||
@ -316,84 +371,107 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
|
|||||||
this.updateDemo();
|
this.updateDemo();
|
||||||
|
|
||||||
const $inputs = {
|
const $inputs = {
|
||||||
$clutchOption: this.$wizardInstructions.querySelector('#clutch-option'),
|
$clutchOption:
|
||||||
$brakeOption: this.$wizardInstructions.querySelector('#brake-option'),
|
this.$wizardInstructions.querySelector("#clutch-option"),
|
||||||
$throttleOption: this.$wizardInstructions.querySelector('#throttle-option'),
|
$brakeOption:
|
||||||
$steeringOption: this.$wizardInstructions.querySelector('#steering-option')
|
this.$wizardInstructions.querySelector("#brake-option"),
|
||||||
|
$throttleOption:
|
||||||
|
this.$wizardInstructions.querySelector("#throttle-option"),
|
||||||
|
$steeringOption:
|
||||||
|
this.$wizardInstructions.querySelector("#steering-option"),
|
||||||
};
|
};
|
||||||
const $chartOption = this.$wizardInstructions.querySelector('#chart-option');
|
const $chartOption =
|
||||||
const $historyOption = this.$wizardInstructions.querySelector('#history-option');
|
this.$wizardInstructions.querySelector("#chart-option");
|
||||||
const $metersOption = this.$wizardInstructions.querySelector('#meters-option');
|
const $historyOption =
|
||||||
const $steeringAngleOption = this.$wizardInstructions.querySelector('#steering-angle-option');
|
this.$wizardInstructions.querySelector("#history-option");
|
||||||
const $fpsOption = this.$wizardInstructions.querySelector('[name=fps-option]');
|
const $metersOption =
|
||||||
|
this.$wizardInstructions.querySelector("#meters-option");
|
||||||
|
const $steeringAngleOption = this.$wizardInstructions.querySelector(
|
||||||
|
"#steering-angle-option",
|
||||||
|
);
|
||||||
|
const $fpsOption =
|
||||||
|
this.$wizardInstructions.querySelector("[name=fps-option]");
|
||||||
|
|
||||||
$inputs.$clutchOption.addEventListener('change', () => {
|
$inputs.$clutchOption.addEventListener("change", () => {
|
||||||
this.withClutch = $inputs.$clutchOption.checked;
|
this.withClutch = $inputs.$clutchOption.checked;
|
||||||
if (this.withClutch) {
|
if (this.withClutch) {
|
||||||
this.$clutch.style.display = '';
|
this.$clutch.style.display = "";
|
||||||
} else {
|
} else {
|
||||||
this.$clutch.style.display = 'none';
|
this.$clutch.style.display = "none";
|
||||||
for (const data of this.chartData) { data.clutch = 0; }
|
for (const data of this.chartData) {
|
||||||
|
data.clutch = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
$inputs.$brakeOption.addEventListener('change', () => {
|
$inputs.$brakeOption.addEventListener("change", () => {
|
||||||
this.withBrake = $inputs.$brakeOption.checked;
|
this.withBrake = $inputs.$brakeOption.checked;
|
||||||
if (this.withBrake) {
|
if (this.withBrake) {
|
||||||
this.$brake.style.display = '';
|
this.$brake.style.display = "";
|
||||||
} else {
|
} else {
|
||||||
this.$brake.style.display = 'none';
|
this.$brake.style.display = "none";
|
||||||
for (const data of this.chartData) { data.brake = 0; }
|
for (const data of this.chartData) {
|
||||||
|
data.brake = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
$inputs.$throttleOption.addEventListener('change', () => {
|
$inputs.$throttleOption.addEventListener("change", () => {
|
||||||
this.withThrottle = $inputs.$throttleOption.checked;
|
this.withThrottle = $inputs.$throttleOption.checked;
|
||||||
if (this.withThrottle) {
|
if (this.withThrottle) {
|
||||||
this.$throttle.style.display = '';
|
this.$throttle.style.display = "";
|
||||||
} else {
|
} else {
|
||||||
this.$throttle.style.display = 'none';
|
this.$throttle.style.display = "none";
|
||||||
for (const data of this.chartData) { data.throttle = 0; }
|
for (const data of this.chartData) {
|
||||||
|
data.throttle = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
$inputs.$steeringOption.addEventListener('change', () => {
|
$inputs.$steeringOption.addEventListener("change", () => {
|
||||||
this.withSteering = $inputs.$steeringOption.checked;
|
this.withSteering = $inputs.$steeringOption.checked;
|
||||||
if (this.withSteering) {
|
if (this.withSteering) {
|
||||||
this.$steering.style.display = '';
|
this.$steering.style.display = "";
|
||||||
} else {
|
} else {
|
||||||
this.$steering.style.display = 'none';
|
this.$steering.style.display = "none";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
$chartOption.addEventListener('change', () => {
|
$chartOption.addEventListener("change", () => {
|
||||||
this.withChart = $chartOption.checked;
|
this.withChart = $chartOption.checked;
|
||||||
if (this.withChart) {
|
if (this.withChart) {
|
||||||
this.$chart.style.display = '';
|
this.$chart.style.display = "";
|
||||||
} else {
|
} else {
|
||||||
this.$chart.style.display = 'none';
|
this.$chart.style.display = "none";
|
||||||
}
|
}
|
||||||
this.setupChart(true);
|
this.setupChart(true);
|
||||||
});
|
});
|
||||||
$historyOption.addEventListener('change', () => {
|
$historyOption.addEventListener("change", () => {
|
||||||
this.historyLength = Number.parseInt($historyOption.value) * 1000;
|
this.historyLength = Number.parseInt($historyOption.value) * 1000;
|
||||||
this.setupChart(true);
|
this.setupChart(true);
|
||||||
});
|
});
|
||||||
$metersOption.addEventListener('change', () => {
|
$metersOption.addEventListener("change", () => {
|
||||||
this.withMeters = $metersOption.checked;
|
this.withMeters = $metersOption.checked;
|
||||||
if (this.withMeters) {
|
if (this.withMeters) {
|
||||||
this.$meters.style.display = '';
|
this.$meters.style.display = "";
|
||||||
} else {
|
} else {
|
||||||
this.$meters.style.display = 'none';
|
this.$meters.style.display = "none";
|
||||||
}
|
}
|
||||||
this.setupChart(true);
|
this.setupChart(true);
|
||||||
});
|
});
|
||||||
$steeringAngleOption.addEventListener('change', () => {
|
$steeringAngleOption.addEventListener("change", () => {
|
||||||
this.angle = $steeringAngleOption.value;
|
this.angle = $steeringAngleOption.value;
|
||||||
});
|
});
|
||||||
$fpsOption.addEventListener('change', () => {
|
$fpsOption.addEventListener("change", () => {
|
||||||
this.interval = 1000 / Number.parseInt($fpsOption.value);
|
this.interval = 1000 / Number.parseInt($fpsOption.value);
|
||||||
console.log(this.interval);
|
console.log(this.interval);
|
||||||
this.setupChart(true);
|
this.setupChart(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
return { $inputs, $chartOption, $historyOption, $metersOption, $steeringAngleOption, $fpsOption };
|
return {
|
||||||
|
$inputs,
|
||||||
|
$chartOption,
|
||||||
|
$historyOption,
|
||||||
|
$metersOption,
|
||||||
|
$steeringAngleOption,
|
||||||
|
$fpsOption,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -406,9 +484,10 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
|
|||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const interval = window.setInterval(() => {
|
const interval = window.setInterval(() => {
|
||||||
const gamepad = this.gamepad.getActive();
|
const gamepad = this.gamepad.getActive();
|
||||||
const pressedButton = index !== undefined
|
const pressedButton =
|
||||||
? gamepad.buttons[index].pressed
|
index !== undefined
|
||||||
: gamepad.buttons.some((button) => button.pressed);
|
? gamepad.buttons[index].pressed
|
||||||
|
: gamepad.buttons.some((button) => button.pressed);
|
||||||
if (pressedButton) return;
|
if (pressedButton) return;
|
||||||
window.clearInterval(interval);
|
window.clearInterval(interval);
|
||||||
resolve();
|
resolve();
|
||||||
@ -425,7 +504,9 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
|
|||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const pressInterval = window.setInterval(() => {
|
const pressInterval = window.setInterval(() => {
|
||||||
const gamepad = this.gamepad.getActive();
|
const gamepad = this.gamepad.getActive();
|
||||||
const index = gamepad.buttons.findIndex((button) => button.pressed);
|
const index = gamepad.buttons.findIndex(
|
||||||
|
(button) => button.pressed,
|
||||||
|
);
|
||||||
if (index === -1) return;
|
if (index === -1) return;
|
||||||
window.clearInterval(pressInterval);
|
window.clearInterval(pressInterval);
|
||||||
const releaseInterval = window.setInterval(() => {
|
const releaseInterval = window.setInterval(() => {
|
||||||
@ -457,7 +538,10 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
|
|||||||
value = gamepad.axes[index];
|
value = gamepad.axes[index];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (referenceValue !== undefined && Math.abs(gamepad.axes[index] - referenceValue) < 0.5) {
|
if (
|
||||||
|
referenceValue !== undefined &&
|
||||||
|
Math.abs(gamepad.axes[index] - referenceValue) < 0.5
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (Math.abs(gamepad.axes[index] - value) > 0.05) {
|
if (Math.abs(gamepad.axes[index] - value) > 0.05) {
|
||||||
@ -556,21 +640,25 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
|
|||||||
$historyOption,
|
$historyOption,
|
||||||
$metersOption,
|
$metersOption,
|
||||||
$steeringAngleOption,
|
$steeringAngleOption,
|
||||||
$fpsOption
|
$fpsOption,
|
||||||
} = this.setupWizard();
|
} = this.setupWizard();
|
||||||
await this.waitButtonRelease();
|
await this.waitButtonRelease();
|
||||||
await this.waitButtonClick();
|
await this.waitButtonClick();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...this.AXES.reduce((options, axis) => Object.assign(
|
...this.AXES.reduce(
|
||||||
options,
|
(options, axis) =>
|
||||||
{ [`with${this.capitalize(axis)}`]: $inputs[`$${axis}Option`].checked }
|
Object.assign(options, {
|
||||||
), {}),
|
[`with${this.capitalize(axis)}`]:
|
||||||
|
$inputs[`$${axis}Option`].checked,
|
||||||
|
}),
|
||||||
|
{},
|
||||||
|
),
|
||||||
chart: $chartOption.checked,
|
chart: $chartOption.checked,
|
||||||
history: Number.parseInt($historyOption.value) * 1000,
|
history: Number.parseInt($historyOption.value) * 1000,
|
||||||
meters: $metersOption.checked,
|
meters: $metersOption.checked,
|
||||||
angle: $steeringAngleOption.value,
|
angle: $steeringAngleOption.value,
|
||||||
fps: Number.parseInt($fpsOption.value)
|
fps: Number.parseInt($fpsOption.value),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -586,16 +674,25 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
|
|||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const interval = window.setInterval(async () => {
|
const interval = window.setInterval(async () => {
|
||||||
const gamepad = this.gamepad.getActive();
|
const gamepad = this.gamepad.getActive();
|
||||||
const buttonIndex = ['button', undefined].includes(type)
|
const buttonIndex = ["button", undefined].includes(type)
|
||||||
? gamepad.buttons.findIndex((button, index) => button.pressed && Math.abs(button.value - before.buttons[index].value) > distance)
|
? gamepad.buttons.findIndex(
|
||||||
|
(button, index) =>
|
||||||
|
button.pressed &&
|
||||||
|
Math.abs(
|
||||||
|
button.value - before.buttons[index].value,
|
||||||
|
) > distance,
|
||||||
|
)
|
||||||
: -1;
|
: -1;
|
||||||
const axisIndex = ['axis', undefined].includes(type)
|
const axisIndex = ["axis", undefined].includes(type)
|
||||||
? gamepad.axes.findIndex((axis, index) => Math.abs(axis - before.axes[index]) > distance)
|
? gamepad.axes.findIndex(
|
||||||
|
(axis, index) =>
|
||||||
|
Math.abs(axis - before.axes[index]) > distance,
|
||||||
|
)
|
||||||
: -1;
|
: -1;
|
||||||
if (buttonIndex === -1 && axisIndex === -1) return;
|
if (buttonIndex === -1 && axisIndex === -1) return;
|
||||||
window.clearInterval(interval);
|
window.clearInterval(interval);
|
||||||
resolve({
|
resolve({
|
||||||
type: buttonIndex === -1 ? 'axis' : 'button',
|
type: buttonIndex === -1 ? "axis" : "button",
|
||||||
index: buttonIndex === -1 ? axisIndex : buttonIndex,
|
index: buttonIndex === -1 ? axisIndex : buttonIndex,
|
||||||
});
|
});
|
||||||
}, 100);
|
}, 100);
|
||||||
@ -613,7 +710,7 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
|
|||||||
<p>Waiting for <strong>${name}</strong> activity.</p>
|
<p>Waiting for <strong>${name}</strong> activity.</p>
|
||||||
`;
|
`;
|
||||||
const { type, index } = await this.detectActivity();
|
const { type, index } = await this.detectActivity();
|
||||||
if (type === 'button') {
|
if (type === "button") {
|
||||||
this.$wizardInstructions.innerHTML = `
|
this.$wizardInstructions.innerHTML = `
|
||||||
<p>Release the <strong>${name}</strong> button.</p>
|
<p>Release the <strong>${name}</strong> button.</p>
|
||||||
`;
|
`;
|
||||||
@ -643,7 +740,7 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
|
|||||||
this.$wizardInstructions.innerHTML = `
|
this.$wizardInstructions.innerHTML = `
|
||||||
<p>Turn the <strong>steering</strong> axis all the way to the <strong>left</strong>.</p>
|
<p>Turn the <strong>steering</strong> axis all the way to the <strong>left</strong>.</p>
|
||||||
`;
|
`;
|
||||||
const { index } = await this.detectActivity('axis', 0.2);
|
const { index } = await this.detectActivity("axis", 0.2);
|
||||||
const leftValue = await this.getAxisPush(index, 0);
|
const leftValue = await this.getAxisPush(index, 0);
|
||||||
|
|
||||||
this.$wizardInstructions.innerHTML = `
|
this.$wizardInstructions.innerHTML = `
|
||||||
@ -664,7 +761,7 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
|
|||||||
return Object.entries(options)
|
return Object.entries(options)
|
||||||
.filter(([, value]) => value !== undefined && value !== null)
|
.filter(([, value]) => value !== undefined && value !== null)
|
||||||
.map(([key, value]) => `${key}=${value}`)
|
.map(([key, value]) => `${key}=${value}`)
|
||||||
.join('&');
|
.join("&");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -694,22 +791,35 @@ window.gamepad.TemplateClass = class TelemetryTemplate {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
async wizard() {
|
async wizard() {
|
||||||
this.$wizard.classList.add('active');
|
this.$wizard.classList.add("active");
|
||||||
|
|
||||||
const gamepad = this.gamepad.getActive();
|
const gamepad = this.gamepad.getActive();
|
||||||
const { withClutch, withBrake, withThrottle, withSteering, chart, history, meters, angle, fps } = await this.askForOptions();
|
const {
|
||||||
const clutch = withClutch && await this.calibratePedal('clutch');
|
withClutch,
|
||||||
const brake = withBrake && await this.calibratePedal('brake');
|
withBrake,
|
||||||
const throttle = withThrottle && await this.calibratePedal('throttle');
|
withThrottle,
|
||||||
const steering = withSteering && await this.calibrateSteering();
|
withSteering,
|
||||||
|
chart,
|
||||||
|
history,
|
||||||
|
meters,
|
||||||
|
angle,
|
||||||
|
fps,
|
||||||
|
} = await this.askForOptions();
|
||||||
|
const clutch = withClutch && (await this.calibratePedal("clutch"));
|
||||||
|
const brake = withBrake && (await this.calibratePedal("brake"));
|
||||||
|
const throttle =
|
||||||
|
withThrottle && (await this.calibratePedal("throttle"));
|
||||||
|
const steering = withSteering && (await this.calibrateSteering());
|
||||||
|
|
||||||
window.location.href = [
|
window.location.href = [
|
||||||
`?gamepad=${gamepad.id}&type=telemetry`,
|
`?gamepad=${gamepad.id}&type=telemetry`,
|
||||||
this.toOptionsParams({ chart, history, meters, angle, fps }),
|
this.toOptionsParams({ chart, history, meters, angle, fps }),
|
||||||
withClutch ? this.toPedalParams('clutch', clutch) : null,
|
withClutch ? this.toPedalParams("clutch", clutch) : null,
|
||||||
withBrake ? this.toPedalParams('brake', brake) : null,
|
withBrake ? this.toPedalParams("brake", brake) : null,
|
||||||
withThrottle ? this.toPedalParams('throttle', throttle) : null,
|
withThrottle ? this.toPedalParams("throttle", throttle) : null,
|
||||||
withSteering ? this.toSteeringParams(steering) : null
|
withSteering ? this.toSteeringParams(steering) : null,
|
||||||
].filter(e => e !== null).join('&');
|
]
|
||||||
|
.filter((e) => e !== null)
|
||||||
|
.join("&");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -22,8 +22,18 @@
|
|||||||
<span class="button y" data-button="3"></span>
|
<span class="button y" data-button="3"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="sticks">
|
<div class="sticks">
|
||||||
<span class="stick left" data-button="10" data-axis-x="0" data-axis-y="1"></span>
|
<span
|
||||||
<span class="stick right" data-button="11" data-axis-x="2" data-axis-y="3"></span>
|
class="stick left"
|
||||||
|
data-button="10"
|
||||||
|
data-axis-x="0"
|
||||||
|
data-axis-y="1"
|
||||||
|
></span>
|
||||||
|
<span
|
||||||
|
class="stick right"
|
||||||
|
data-button="11"
|
||||||
|
data-axis-x="2"
|
||||||
|
data-axis-y="3"
|
||||||
|
></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="dpad">
|
<div class="dpad">
|
||||||
<span class="face up" data-button="12"></span>
|
<span class="face up" data-button="12"></span>
|
||||||
|
@ -17,26 +17,41 @@ window.gamepad.TemplateClass = class XboxOneTemplate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateButton($button, button) {
|
updateButton($button, button) {
|
||||||
if (!$button.matches('.trigger') || !button) return;
|
if (!$button.matches(".trigger") || !button) return;
|
||||||
$button.style.setProperty('opacity', this.gamepad.useMeterTriggers ? 1 : `${button.value * 100}%`);
|
$button.style.setProperty(
|
||||||
$button.style.setProperty('clip-path', this.gamepad.useMeterTriggers ? `inset(${100 - button.value * 100}% 0px 0px 0pc)` : 'none');
|
"opacity",
|
||||||
|
this.gamepad.useMeterTriggers ? 1 : `${button.value * 100}%`,
|
||||||
|
);
|
||||||
|
$button.style.setProperty(
|
||||||
|
"clip-path",
|
||||||
|
this.gamepad.useMeterTriggers
|
||||||
|
? `inset(${100 - button.value * 100}% 0px 0px 0pc)`
|
||||||
|
: "none",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateAxis($axis, attribute, axis) {
|
updateAxis($axis, attribute, axis) {
|
||||||
if (!$axis.matches('.stick')) return;
|
if (!$axis.matches(".stick")) return;
|
||||||
if (attribute === 'data-axis-x') {
|
if (attribute === "data-axis-x") {
|
||||||
$axis.style.setProperty('margin-left', `${axis * 25}px`);
|
$axis.style.setProperty("margin-left", `${axis * 25}px`);
|
||||||
this.updateRotate($axis);
|
this.updateRotate($axis);
|
||||||
}
|
}
|
||||||
if (attribute === 'data-axis-y') {
|
if (attribute === "data-axis-y") {
|
||||||
$axis.style.setProperty('margin-top', `${axis * 25}px`);
|
$axis.style.setProperty("margin-top", `${axis * 25}px`);
|
||||||
this.updateRotate($axis);
|
this.updateRotate($axis);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateRotate($axis) {
|
updateRotate($axis) {
|
||||||
const rotateX = Number.parseFloat($axis.getAttribute('data-value-y') * 30);
|
const rotateX = Number.parseFloat(
|
||||||
const rotateY = -Number.parseFloat($axis.getAttribute('data-value-x') * 30);
|
$axis.getAttribute("data-value-y") * 30,
|
||||||
$axis.style.setProperty('transform', `rotateX(${rotateX}deg) rotateY(${rotateY}deg)`);
|
);
|
||||||
|
const rotateY = -Number.parseFloat(
|
||||||
|
$axis.getAttribute("data-value-x") * 30,
|
||||||
|
);
|
||||||
|
$axis.style.setProperty(
|
||||||
|
"transform",
|
||||||
|
`rotateX(${rotateX}deg) rotateY(${rotateY}deg)`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|