diff --git a/index.html b/index.html
index 68cfb7e..62ec6e3 100644
--- a/index.html
+++ b/index.html
@@ -145,7 +145,12 @@
[Exposed=Window, SecureContext]
- interface Gamepad {
+ interface Gamepad : EventTarget {
+ attribute EventHandler onbuttondown;
+ attribute EventHandler onbuttonup;
+ attribute EventHandler ongamepadchange;
+ attribute EventHandler onrawgamepadchange;
+
readonly attribute DOMString id;
readonly attribute long index;
readonly attribute boolean connected;
@@ -301,8 +306,47 @@
A [=list=] containing the maximum logical value for each button
+
+
+ [[\pendingChanges]]
+ |
+
+ An empty [=list=]
+ |
+
+ A [=list=] containing pending {{GamepadChangeEvent}}s.
+ |
+
+ -
+ onbuttondown attribute
+
+ -
+ {{Gamepad/onbuttondown}} is an [=event handler IDL attribute=] for
+ the {{buttondown}} event type.
+
+ -
+ onbuttonup attribute
+
+ -
+ {{Gamepad/onbuttonup}} is an [=event handler IDL attribute=] for the
+ {{buttonup}} event type.
+
+ -
+ ongamepadchange attribute
+
+ -
+ {{Gamepad/ongamepadchange}} is an [=event handler IDL attribute=] for
+ the {{gamepadchange}} event type.
+
+ -
+ onrawgamepadchange attribute
+
+ -
+ {{Gamepad/onrawgamepadchange}} is an [=event handler IDL attribute=]
+ for the {{rawgamepadchange}} event type.
+
-
id attribute
@@ -469,6 +513,26 @@
following steps:
+ - Initialize |oldAxisValues:list| to be an empty [=list=].
+
+ - [=list/For each=] |axis:double| of |gamepad|.{{Gamepad/axes}},
+ [=list/append=] |axis| to |oldAxisValues|.
+
+ - Initialize |oldButtonValues:list| to be an empty [=list=].
+
+ - Initialize |oldButtonPressed:list| to be an empty [=list=].
+
+ - [=list/For each=] |button:GamepadButton| of
+ |gamepad|.{{Gamepad/buttons}}:
+
+ - [=list/Append=] |button|.{{GamepadButton/value}} to
+ |oldButtonValues|.
+
+ - [=list/Append=] |button|.{{GamepadButton/pressed}} to
+ |oldButtonPressed|.
+
+
+
- Let |now:DOMHighResTimeStamp| be the [=current high resolution
time=].
@@ -503,11 +567,11 @@
`null`.
- If |document| is not `null` and is [=Document/fully
- active=], then [=queue a task=] on the [=gamepad task
- source=] to [=fire an event=] named {{gamepadconnected}}
- at |gamepad|'s [=relevant global object=] using
- {{GamepadEvent}} with its {{GamepadEvent/gamepad}}
- attribute initialized to |connectedGamepad|.
+ active=], then [=fire an event=] named
+ {{gamepadconnected}} at |gamepad|'s [=relevant global
+ object=] using {{GamepadEvent}} with its
+ {{GamepadEvent/gamepad}} attribute initialized to
+ |connectedGamepad|.
@@ -515,7 +579,211 @@
+
If |navigator|.{{Navigator/[[hasGamepadGesture]]}} is `false`,
+ abort these steps.
+
+ Let |axesChanged| be an empty [=list=].
+
+ [=list/For each=] |axisIndex:long| of [=the range=] from 0 to the
+ [=list/size=] of |gamepad|.{{Gamepad/axes}} − 1:
+
+ - If |oldAxisValues|[|axisIndex|] is not equal to
+ |gamepad|.{{Gamepad/axes}}[|axisIndex|], [=list/append=]
+ |axisIndex| to |axesChanged|.
+
+
+
+ Let |buttonsChanged| be an empty [=list=].
+
+ Let |buttonsPressed| be an empty [=list=].
+
+ Let |buttonsReleased| be an empty [=list=].
+
+ [=list/For each=] |buttonIndex:long| of [=the range=] from 0 to
+ the [=list/size=] of |gamepad|.{{Gamepad/buttons}} − 1:
+
+ - Let |button:GamepadButton| be
+ |gamepad|.{{Gamepad/buttons}}[|buttonIndex|].
+
+ - If |oldButtonValues|[|buttonIndex|] is not equal to
+ |button|.{{GamepadButton/value}}, [=list/append=] |buttonIndex|
+ to |buttonsChanged|.
+
+ - If |oldButtonPressed|[|buttonIndex|] is `false` and
+ |button|.{{GamepadButton/pressed}} is `true`, [=list/append=]
+ |buttonIndex| to |buttonsPressed|.
+
+ - If |oldButtonPressed|[|buttonIndex|] is `true` and
+ |button|.{{GamepadButton/pressed}} is `false`, [=list/append=]
+ |buttonIndex| to |buttonsReleased|.
+
+
+
+ If any of |axesChanged| or |buttonsChanged|, |buttonsPressed|, or
+ |buttonsReleased| [=list/is not empty=]:
+
+ - Let |gamepadSnapshot| be the result of [=creating a
+ snapshot=] for |gamepad|.
+
+ - [=Fire an event=] named {{rawgamepadchange}} at |gamepad|
+ using {{GamepadChangeEvent}} with its
+ {{GamepadChangeEvent/gamepadSnapshot}} attribute initialized to
+ |gamepadSnapshot|, its {{GamepadChangeEvent/axesChanged}}
+ attribute initialized to |axesChanged|, its
+ {{GamepadChangeEvent/buttonsChanged}} attribute initialized to
+ |buttonsChanged|, its {{GamepadChangeEvent/buttonsPressed}}
+ attribute initialized to |buttonsPressed|, and its
+ {{GamepadChangeEvent/buttonsReleased}} attribute initialized to
+ |buttonsReleased|.
+
+ - Let |event:GamepadChangeEvent| be a newly created
+ {{GamepadChangeEvent}} with its
+ {{GamepadChangeEvent/gamepadSnapshot}} attribute initialized to
+ |gamepadSnapshot|, its {{GamepadChangeEvent/axesChanged}}
+ attribute initialized to |axesChanged|, its
+ {{GamepadChangeEvent/buttonsChanged}} attribute initialized to
+ |buttonsChanged|, its {{GamepadChangeEvent/buttonsPressed}}
+ attribute initialized to |buttonsPressed|, and its
+ {{GamepadChangeEvent/buttonsReleased}} attribute initialized to
+ |buttonsReleased|.
+
+ - Append |event| to |gamepad|.{{Gamepad/[[pendingChanges]]}}.
+
+
+
+ If |buttonsPressed| or |buttonsReleased| [=list/is not empty=],
+ [=flush pending change events=] for |gamepad|.
+
+ [=list/For each=] |buttonIndex:long| of |buttonsPressed|:
+
+ - [=Fire an event=] named {{buttondown}} at |gamepad| using
+ {{GamepadButtonEvent}} with its
+ {{GamepadButtonEvent/gamepadIndex}} attribute initialized to
+ |gamepad|.{{Gamepad/index}}, its
+ {{GamepadButtonEvent/buttonIndex}} attribute initialized to
+ |buttonIndex|, its {{GamepadButtonEvent/buttonSnapshot}}
+ attribute initialized to the result of [=creating a button
+ snapshot=] of |gamepad|.{{Gamepad/buttons}}[|buttonIndex|], and
+ its {{GamepadButtonEvent/gamepadTimestamp}} attribute initialized
+ to |now|.
+
+
+
+ [=list/For each=] |buttonIndex:long| of |buttonsReleased|:
+
+ - [=Fire an event=] named {{buttonup}} at |gamepad| using
+ {{GamepadButtonEvent}} with its
+ {{GamepadButtonEvent/gamepadIndex}} attribute initialized to
+ |gamepad|.{{Gamepad/index}}, its
+ {{GamepadButtonEvent/buttonIndex}} attribute initialized to
+ |buttonIndex|, its {{GamepadButtonEvent/buttonSnapshot}}
+ attribute initialized to the result of [=creating a button
+ snapshot=] of |gamepad|.{{Gamepad/buttons}}[|buttonIndex|], and
+ its {{GamepadButtonEvent/gamepadTimestamp}} attribute initialized
+ to |now|.
+
+
+
+
+ To flush pending change events for |gamepad:Gamepad|, run
+ the following steps:
+
+
+ - If |gamepad|.{{Gamepad/[[pendingChanges]]}} [=list/is empty=],
+ abort these steps.
+
+ - Let |axesChanged:ordered set| be an empty [=ordered set=].
+
+ - Let |buttonsChanged:ordered set| be an empty [=ordered set=].
+
+ - Let |buttonsPressed:ordered set| be an empty [=ordered set=].
+
+ - Let |buttonsReleased:ordered set| be an empty [=ordered set=].
+
+ - Let |coalescedEvents:list| be an empty [=list=].
+
+ - [=list/For each=] |event:GamepadChangeEvent| of
+ |gamepad|.{{Gamepad/[[pendingChanges]]}}:
+
+ - [=list/For each=] |axisIndex:long| of
+ |event|.{{GamepadChangeEvent/axesChanged}}, [=list/append=]
+ |axisIndex| to |axesChanged|.
+
+ - [=list/For each=] |buttonIndex:long| of
+ |event|.{{GamepadChangeEvent/buttonsChanged}}, [=list/append=]
+ |buttonIndex| to |buttonsChanged|.
+
+ - [=list/For each=] |buttonIndex:long| of
+ |event|.{{GamepadChangeEvent/buttonsPressed}}, [=list/append=]
+ |buttonIndex| to |buttonsPressed|.
+
+ - [=list/For each=] |buttonIndex:long| of
+ |event|.{{GamepadChangeEvent/buttonsReleased}}, [=list/append=]
+ |buttonIndex| to |buttonsReleased|.
+
+ - [=list/Append=] |event| to |coalescedEvents|.
+
+
+
+ - Let |lastChange:GamepadChangeEvent| be the last [=list/item=] of
+ |gamepad|.{{Gamepad/[[pendingChanges]]}}.
+
+ - Let |event:GamepadChangeEvent| be a newly created
+ {{GamepadChangeEvent}} instance:
+
+ - Initialize |event|.{{GamepadChangeEvent/gamepadSnapshot}} to
+ |lastChange|.{{GamepadChangeEvent/gamepadSnapshot}}.
+
+ - Initialize |event|.{{GamepadChangeEvent/axesChanged}} to the
+ result of [=list/sorting in ascending order=] |axesChanged|.
+
+ - Initialize |event|.{{GamepadChangeEvent/buttonsChanged}} to
+ the result of [=list/sorting in ascending order=]
+ |buttonsChanged|.
+
+ - Initialize |event|.{{GamepadChangeEvent/buttonsPressed}} to
+ the result of [=list/sorting in ascending order=]
+ |buttonsPressed|.
+
+ - Initialize |event|.{{GamepadChangeEvent/buttonsReleased}} to
+ the result of [=list/sorting in ascending order=]
+ |buttonsReleased|.
+
+ - Initialize |event|.{{GamepadChangeEvent/[[coalescedEvents]]}}
+ to |coalescedEvents|.
+
+
+
+ - [=list/Empty=] |gamepad|.{{Gamepad/[[pendingChanges]]}}.
+
+ - [=Dispatch=] |event| to |gamepad|.
+
+
+
+ To run the gamepad steps for a [=document=]
+ |document:document|, run these steps:
+
+
+ - Let |navigator:Navigator| be |document|'s [=relevant global
+ object=]'s {{Navigator}} object.
+
+ - If |navigator|.{{Navigator/[[hasGamepadGesture]]}} is `false`,
+ abort these steps.
+
+ - [=list/For each=] |gamepad:Gamepad?| of
+ |navigator|.{{Navigator/[[gamepads]]}}:
+
+ - If |gamepad| is not `null` and
+ |gamepad|.{{Gamepad/[[exposed]]}} is `true`, [=flush pending
+ change events=] for |gamepad|.
+
+
+
+
+
+ These steps integrate with the event loop defined in HTML. [HTML]
+
To map and normalize axes for |gamepad:Gamepad|, run the
following steps:
@@ -583,7 +851,7 @@
|logicalMinimum|) / (|logicalMaximum| − |logicalMinimum|).
Let |button:GamepadButton| be
- |gamepad|.{{Gamepad/[[buttons]]}}[|mappedIndex|].
+ |gamepad|.{{Gamepad/buttons}}[|mappedIndex|].
Set |button|.{{GamepadButton/[[value]]}} to
|normalizedValue|.
@@ -615,6 +883,66 @@
+
+ To create a button
+ snapshot for |button:GamepadButton|, run the following steps:
+
+
+ - Let |buttonCopy| be a newly created {{GamepadButton}} instance:
+
+ - Initialize |buttonCopy|'s {{GamepadButton/value}} attribute
+ to |button|.{{GamepadButton/value}}.
+
+ - Initialize |buttonCopy|'s {{GamepadButton/pressed}} attribute
+ to |button|.{{GamepadButton/pressed}}.
+
+ - Initialize |buttonCopy|'s {{GamepadButton/touched}} attribute
+ to |button|.{{GamepadButton/touched}}.
+
+
+
+ - Return |buttonCopy|.
+
+
+
+ To create a snapshot for
+ |gamepad:Gamepad|, run the following steps:
+
+
+ - Let |gamepadCopy| be a newly created {{Gamepad}} instance:
+
+ - Initialize |gamepadCopy|.{{Gamepad/id}} to
+ |gamepad|.{{Gamepad/id}}.
+
+ - Initialize |gamepadCopy|.{{Gamepad/index}} to
+ |gamepad|.{{Gamepad/index}}.
+
+ - Initialize |gamepadCopy|.{{Gamepad/mapping}} to
+ |gamepad|.{{Gamepad/mapping}}.
+
+ - Initialize |gamepadCopy|.{{Gamepad/[[connected]]}} to
+ |gamepad|.{{Gamepad/connected}}.
+
+ - Initialize |gamepadCopy|.{{Gamepad/[[timestamp]]}} to
+ |gamepad|.{{Gamepad/timestamp}}.
+
+ - Initialize |gamepadCopy|.{{Gamepad/[[axes]]}} to an empty
+ [=sequence=].
+
+ - [=list/For each=] |axisValue:double| of
+ |gamepad|.{{Gamepad/axes}}, [=list/append=] |axisValue| to
+ |gamepadCopy|.{{Gamepad/[[axes]]}}.
+
+ - [=list/For each=] |button:GamepadButton| of
+ |gamepad|.{{Gamepad/buttons}}, [=create a button snapshot=] for
+ |button| and [=list/append=] it to
+ |gamepadCopy|.{{Gamepad/[[buttons]]}}.
+
+
+
+ - Return |gamepadCopy|.
+
+
@@ -1177,8 +1505,7 @@
[Exposed=Window, SecureContext]
-
- interface GamepadEvent: Event {
+ interface GamepadEvent : Event {
constructor(DOMString type, GamepadEventInit eventInitDict);
[SameObject] readonly attribute Gamepad gamepad;
};
@@ -1211,6 +1538,225 @@
+
+
+ GamepadChangeEvent Interface
+
+
+ [Exposed=Window, SecureContext]
+ interface GamepadChangeEvent : Event {
+ constructor(DOMString type, GamepadChangeEventInit eventInitDict);
+ readonly attribute Gamepad gamepadSnapshot;
+ readonly attribute FrozenArray<long> axesChanged;
+ readonly attribute FrozenArray<long> buttonsChanged;
+ readonly attribute FrozenArray<long> buttonsPressed;
+ readonly attribute FrozenArray<long> buttonsReleased;
+ sequence<GamepadChangeEvent> getCoalescedEvents();
+ };
+
+
+ Instances of {{GamepadChangeEvent}} are created with the internal slots
+ described in the following table:
+
+
+
+
+ Internal slot
+ |
+
+ Initial value
+ |
+
+ Description (non-normative)
+ |
+
+
+
+ [[\coalescedEvents]]
+ |
+
+ An empty [=sequence=]
+ |
+
+ A [=sequence=] of {{GamepadChangeEvent}} instances that were
+ coalesced to create this event.
+ |
+
+
+
+ -
+ gamepadSnapshot attribute
+
+ -
+ A snapshot of the {{Gamepad}} when the event was created.
+
+ -
+ axesChanged attribute
+
+ -
+ An array of indices of items in the {{Gamepad/axes}} array
+ representing the axes that changed.
+
+ -
+ buttonsChanged attribute
+
+ -
+ An array of indices of items in the {{Gamepad/buttons}} array
+ representing the buttons that changed.
+
+ -
+ buttonsPressed attribute
+
+ -
+ An array of indices of items in the {{Gamepad/buttons}} array
+ representing the buttons that were pressed.
+
+ -
+ buttonsReleased attribute
+
+ -
+ An array of indices of items in the {{Gamepad/buttons}} array
+ representing the buttons that were released.
+
+ -
+ getCoalescedEvents() method
+
+ -
+ If this is a {{gamepadchange}} event, returns the [=sequence=] of
+ {{GamepadChangeEvent}} instances that were used to create this event.
+ Otherwise, returns an empty [=sequence=].
+
+
+
+
+ GamepadChangeEventInit dictionary
+
+
+ dictionary GamepadChangeEventInit : EventInit {
+ required Gamepad gamepadSnapshot;
+ required sequence<long> axesChanged;
+ required sequence<long> buttonsChanged;
+ required sequence<long> buttonsPressed;
+ required sequence<long> buttonsReleased;
+ };
+
+
+ -
+ gamepadSnapshot member
+
+ -
+ The gamepad snapshot for the event.
+
+ -
+ axesChanged member
+
+ -
+ The changed axes for the event.
+
+ -
+ buttonsChanged member
+
+ -
+ The changed buttons for the event.
+
+ -
+ buttonsPressed member
+
+ -
+ The pressed buttons for the event.
+
+ -
+ buttonsReleased member
+
+ -
+ The released buttons for the event.
+
+
+
+
+
+
+ GamepadButtonEvent Interface
+
+
+ [Exposed=Window, SecureContext]
+ interface GamepadButtonEvent : Event {
+ constructor(DOMString type, GamepadButtonEventInit eventInitDict);
+ readonly attribute long gamepadIndex;
+ readonly attribute long buttonIndex;
+ readonly attribute GamepadButton buttonSnapshot;
+ readonly attribute DOMHighResTimeStamp gamepadTimestamp;
+ };
+
+
+ -
+ gamepadIndex attribute
+
+ -
+ The {{Gamepad/index}} attribute of the {{Gamepad}} associated with
+ this event.
+
+ -
+ buttonIndex attribute
+
+ -
+ The index of the {{GamepadButton}} in the {{Gamepad/buttons}} array.
+
+ -
+ buttonSnapshot attribute
+
+ -
+ A copy of the {{GamepadButton}} at the time when this event was
+ created.
+
+ -
+ gamepadTimestamp attribute
+
+ -
+ The {{Gamepad/timestamp}} of the {{Gamepad}} associated with this
+ event at the time when the event was created.
+
+
+
+
+ GamepadButtonEventInit dictionary
+
+
+ dictionary GamepadButtonEventInit : EventInit {
+ required long gamepadIndex;
+ required long buttonIndex;
+ required GamepadButton buttonSnapshot;
+ required DOMHighResTimeStamp gamepadTimestamp;
+ };
+
+
+ -
+ gamepadIndex member
+
+ -
+ The gamepad index for the event.
+
+ -
+ buttonIndex member
+
+ -
+ The button index for the event.
+
+ -
+ buttonSnapshot member
+
+ -
+ A copy of the current button state.
+
+ -
+ gamepadTimestamp member
+
+ -
+ The gamepad timestamp for the event.
+
+
+
+
Remapping
@@ -1600,13 +2146,55 @@
- Other events
+ The gamepadchange and rawgamepadchange events
+
+
+ [=User agent=]s implementing this specification MUST provide new DOM
+ events named gamepadchange and rawgamepadchange. These events MUST be of type
+ {{GamepadChangeEvent}} and MUST fire on the {{Gamepad}} object.
+
+
+ When the [=user agent=] receives new button or axis input values from a
+ gamepad, the [=user agent=] MUST compare the current button and axis
+ state with the previous state. If any button or axis has changed, a
+ {{rawgamepadchange}} event MUST be dispatched.
+
+
+ If any button or axis has changed, a pending {{gamepadchange}} event
+ MUST be created and enqueued. The [=user agent=] MAY [=flush pending
+ change events=] at any time after the {{rawgamepadchange}} has been
+ dispatched, but MUST do so before the animation frame callbacks are run
+ in the event loop. [[HTML]]
+
+
+ The {{gamepadchange}} and {{rawgamepadchange}} events MUST NOT be
+ dispatched before the [=user agent=] has dispatched a
+ {{gamepadconnected}} event for the gamepad.
+
+
+
+
+ The buttondown and buttonup events
- More discussion needed, on whether to include or exclude axis and
- button changed events, and whether to roll them more together
- (`"gamepadchanged"`?), separate somewhat (`"gamepadaxischanged"`?), or
- separate by individual axis and button.
+ [=User agent=]s implementing this specification MUST provide new DOM
+ events named buttondown and buttonup. The corresponding events MUST be of type
+ {{GamepadButtonEvent}} and MUST fire on the {{Gamepad}} object.
+
+
+ When the [=user agent=] receives new button or axis input values from a
+ gamepad, the [=user agent=] MUST compare the current button pressed
+ state with the previous button pressed state and dispatch
+ {{buttondown}} and {{buttonup}} events.
+
+
+ The {{buttondown}} and {{buttonup}} events MUST NOT be dispatched
+ before the [=user agent=] has dispatched a {{gamepadconnected}} event
+ for the gamepad. The [=user agent=] MUST [=flush pending change
+ events=] for a gamepad before firing {{buttondown}} or {{buttonup}} for
+ a button on that gamepad.