3 /** DOM Event shim for Web Gamepad API
4 https://dvcs.w3.org/hg/gamepad/raw-file/default/gamepad.html
6 This adds three new custom events to the window object:
8 yuugamepadbuttondown, yuugamepadbuttonup
9 Dispatched when a button on a gamepad is pressed
12 .detail.gamepad - the Gamepad instance with the button
13 .detail.button - the numeric index of the button
14 .detail.mapped - true if the button is known to fit the
15 "standard" mapping, even if gamepad.mapping
19 Dispatched when an axis on a gamepad changes.
21 .detail.gamepad - the Gamepad instance with the axis
22 .detail.axis - the numeric index of the axis
23 .detail.value - the value of the axis
24 .detail.mapped - true if the axis is known to fit the
25 "standard" mapping, even if gamepad.mapping
28 It also polyfills navigator.getGamepads.
30 Including this file (e.g. in a <script> tag) is all that's
31 necessary to enable these.
33 Because the underlying API is poll-based, events will be
34 dispatched at most at the animation refresh rate. Inputs that
35 exist for less than this time may be dropped.
38 function empty () { return []; }
40 if (!navigator
.getGamepads
)
41 navigator
.getGamepads
= (
42 navigator
.webkitGetGamepads
43 || navigator
.mozGetGamepads
45 if (navigator
.getGamepads
=== empty
)
48 var requestFrame
= window
.requestAnimationFrame
49 || window
.mozRequestAnimationFrame
50 || window
.webkitRequestAnimationFrame
;
52 var PLATFORM
= (navigator
.platform
|| "Unknown") + ".";
55 // Maps the logical controller button/axis (the index) to the
56 // equivalent standard controller button/axis (the value),
59 // This needs to be keyed to the underlying platform as well
60 // because mapping varies based on the USB stack in question.
61 // In theory this can go as far as multiple drivers for one OS
62 // with different mappings, but most platforms / devices have
63 // one canonical driver.
64 'MacIntel.54c-268-PLAYSTATION(R)3 Controller': {
65 buttons
: [8, 10, 11, 9, 12, 15, 13, 14, 6, 7,
66 4, 5, 3, 1, 0, 2, 16],
73 function isPressed (button
) {
74 return isFinite(button
) ? button
> 0.1 : button
.pressed
;
77 function isAppropriateState (state
, gamepad
) {
79 && state
.id
=== gamepad
.id
80 && state
.buttons
.length
=== gamepad
.buttons
.length
81 && state
.axes
.length
=== gamepad
.axes
.length
;
84 function makeState (gamepad
) {
86 for (var i
= 0; i
< gamepad
.buttons
.length
; ++i
)
87 buttons
.push(isPressed(gamepad
.buttons
[i
]));
90 axes
: gamepad
.axes
.slice(),
95 function getState (gamepad
) {
96 var state
= states
[gamepad
.index
];
97 // Browsers that do not support the connected/disconnected
98 // events will silently swap in a new gamepad. Try to detect
99 // that and reset the state.
100 if (!isAppropriateState(state
, gamepad
))
101 state
= states
[gamepad
.index
] = makeState(gamepad
);
105 function connected (event
) {
106 states
[event
.gamepad
.index
] = makeState(event
.gamepad
);
109 function disconnected (event
) {
110 delete states
[event
.gamepad
.index
];
113 function buttonEvent (gamepad
, button
, pressed
) {
114 var mapping
= MAPPINGS
[PLATFORM
+ gamepad
.id
];
115 var mapped
= gamepad
.mapping
=== "standard" || !!mapping
;
116 if (gamepad
.mapping
!== "standard" && mapping
)
117 button
= mapping
.buttons
[button
];
119 return new CustomEvent(
120 pressed
? 'yuugamepadbuttondown' : 'yuugamepadbuttonup',
121 { detail
: { gamepad
: gamepad
, mapped
: mapped
, button
: button
} });
124 function axisEvent (gamepad
, axis
, value
) {
125 var mapping
= MAPPINGS
[PLATFORM
+ gamepad
.id
];
126 var mapped
= gamepad
.mapping
=== "standard" || !!mapping
;
127 if (gamepad
.mapping
!== "standard" && mapping
)
128 axis
= mapping
.axes
[axis
];
130 return new CustomEvent(
131 'yuugamepadaxismove',
132 { detail
: { gamepad
: gamepad
, mapped
: mapped
,
133 axis
: axis
, value
: value
} });
136 function pumpGamepad (gamepad
) {
137 var state
= getState(gamepad
);
138 for (var b
= 0; b
< gamepad
.buttons
.length
; ++b
) {
139 var wasPressed
= isPressed(state
.buttons
[b
]);
140 var nowPressed
= isPressed(gamepad
.buttons
[b
]);
141 if (wasPressed
!== nowPressed
) {
142 state
.buttons
[b
] = nowPressed
;
143 window
.dispatchEvent(buttonEvent(gamepad
, b
, nowPressed
));
146 for (var a
= 0; a
< gamepad
.axes
.length
; ++a
) {
147 if (gamepad
.axes
[a
] !== state
.axes
[a
]) {
148 var value
= state
.axes
[a
] = gamepad
.axes
[a
];
149 window
.dispatchEvent(axisEvent(gamepad
, a
, value
));
155 var gamepads
= navigator
.getGamepads();
156 for (var i
= 0; i
< gamepads
.length
; ++i
) {
157 var gamepad
= gamepads
[i
];
160 pumpGamepad(gamepad
);
164 requestFrame
.call(window
, function _ () {
166 requestFrame
.call(window
, _
);
169 window
.addEventListener('gamepadconnected', connected
);
170 window
.addEventListener('gamepaddisconnected', disconnected
);