|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
// An input device may be divided in multiple parts; a mouse has buttons and a 2D slider -- a trackpad may have a touch surface and buttons |
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
class InputDevice { |
|
|
|
buttons: Array<InputDeviceButton> // all the buttons of the device |
|
sliders: Array<InputDeviceSlider> // all the sliders of the device |
|
surfaces: Array<InputDeviceSurface> // all the surfaces of the device |
|
|
|
parts: Array<InputDevicePart> // all the previously included devices (and more if more types are added in later revisions of the spec) |
|
|
|
isEmulated: boolean // returns true if the device is as a whole a mupped of another device (like a keyboard is emulated by an onscreen-visual) |
|
|
|
} |
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
// Each part of an input device is attached to its parent device, and may emulate or be emulated by other parts |
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
class InputDevicePart { |
|
|
|
// the device in which the part is defined |
|
parentDevice: InputDevice |
|
|
|
// returns true if the device is auto-generated from another device (like a mouse is emulated by a trackpad) |
|
isEmulated: boolean |
|
emulationControler: InputDevicePart // if known, the device part which controls this device as a muppet |
|
|
|
// the devices which are emulated from this one |
|
muppetDevices: Array<InputDevicePart> |
|
|
|
} |
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
// devices mappable to multitouch devices |
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
interface ContactCompatibleInputDevice { |
|
|
|
//-------------------------------- |
|
// device properties |
|
//-------------------------------- |
|
canFireContactEvents: boolean // some devices cannot generate contacts, like a Kinect 1 or a webcam |
|
contactsSupportPressure: boolean // some properties about the contacts which are going to be generated by this device |
|
contactsSupportAngle: boolean // (see PointerEvents for more info and properties) |
|
contactsSupportHover: boolean // (...) |
|
// etc... |
|
|
|
surfaceWidth: double // the width of the surface, as reported in the contact events |
|
surfaceHeight: double // the height of the surface, as reported in the contact events |
|
mappedScreenSurface: Rect<double> // if the device is mapped to the screen, which region it is mapped to (otherwhise: null) |
|
|
|
//-------------------------------- |
|
// device events |
|
//-------------------------------- |
|
isContactEnabledByDefault: boolean |
|
@Writable isContactEnabled: boolean // some devices which can generate contact may be switched to bitmap only for performance reasons |
|
oncontactsadded: Function<ContactCompatibleInputDeviceEvent> // similar to "touchstart" |
|
oncontactsremoved: Function<ContactCompatibleInputDeviceEvent> // similar to "touchend" |
|
oncontactsupdated: Function<ContactCompatibleInputDeviceEvent> // similar to "touchmove" |
|
|
|
//-------------------------------- |
|
// device state |
|
//-------------------------------- |
|
lastContacts: Array<InputDeviceContact> // values as cached from the last fired InputDeviceSurfaceEvent |
|
|
|
//------------------------------- |
|
// dom events generation |
|
//------------------------------- |
|
firesTouchEventsByDefault: boolean |
|
firesPointerEventsByDefault: boolean |
|
@Writable firesTouchEvents: boolean // even if the devices can or does fires contact events, some may not be interested in mapping those to touch events |
|
@Writable firesPointerEvents: boolean // even if the devices can or does generates contact events, some may not be interested in mapping those to pointers events |
|
|
|
} |
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
// devices mappable to a mouse wheel |
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
interface MouseWheelCompatibleInputDevice { |
|
|
|
//-------------------------------- |
|
// device properties (stable) |
|
//-------------------------------- |
|
canFireMouseWheelEvents: boolean // probably true anyway (we can use any slider as a wheel by considering its value as a delta (or rotation speed) each time, can't we?) |
|
|
|
//------------------------------------- |
|
// device properties (stable) |
|
//------------------------------------- |
|
dimension: int // equal to 1 for (x) sliders like mousewheel, 2 for (x,y) sliders like mouses, 3 for (x,y,z) sliders like 3d mouses |
|
|
|
//------------------------------------- |
|
// device state (transient) |
|
//------------------------------------- |
|
x: double // the first value of the slider/wheel // mouse wheels usually have only one dimension |
|
y: double // the second value of the slider/wheel, if any (orelse 0); // mouse-move wheels have two dimensions |
|
z: double // the third value of the slider, if any (orelse 0); // 3d-mouse-move wheels have three dimensions |
|
|
|
//------------------------------------- |
|
// device properties (continued) |
|
//------------------------------------- |
|
|
|
minX: double // the minimal value for the slider x-value, of -Infty |
|
maxX: double // the minimal value for the slider y-value, of +Infty |
|
stepX: double // the minimal increment for the x-value (or 0 if no guarantee) |
|
// ditto for y and z |
|
|
|
//-------------------------------- |
|
// device events |
|
//-------------------------------- |
|
onupdated: Function<MouseWheelCompatibleInputDeviceEvent> |
|
|
|
//------------------------------- |
|
// dom events generation |
|
//------------------------------- |
|
firesMouseWheelEventsByDefault: boolean |
|
@Writable firesMouseWheelEvents: boolean |
|
// true if the device is used to generate wheel events (the default value can usually be overwritten) |
|
// NOTE: a device which is not reset between changes will work like the emulated thumb wheel generated using the mouse position on wheel click |
|
// NOTE: some devices may switch modes for real (like a mouse after PointerLock) |
|
|
|
@Writable mouseEventsPointer: PersistentPointer // see later (the pointer for which the wheel events will be generated) |
|
|
|
} |
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
// devices mapped to a mouse-move wheel |
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
interface MouseMoveCompatibleInputDevice extends MouseWheelCompatibleInputDevice { |
|
|
|
//-------------------------------- |
|
// device properties (stable) |
|
//-------------------------------- |
|
canFireMouseMoveEvents: boolean // probably true anyway (we can use any slider as a wheel by considering its value as a delta (or rotation speed) each time, can't we?) |
|
|
|
//------------------------------------- |
|
// device properties (stable) |
|
//------------------------------------- |
|
xDevicePixelRatioByDefault: boolean // if x is expressed relatively to the device, by which factor % css pixels (normal zoom window) |
|
@Writable xDevicePixelRatio: double // if x is expressed relatively to the device, by which factor % css pixels (normal zoom window) |
|
xScreenZoneMin: double // relatively to the screen regions, which part of the screen is mapped to the device: usually minX (touch screens), or NaN (trackpads/mouses) as the pointer can sense moves outside the acceptable area |
|
xScreenZoneMax: double // relatively to the screen regions, which part of the screen is mapped to the device: usually maxX (touch screens), or NaN (trackpads/mouses) as the pointer can sense moves outside the acceptable area |
|
// ditto for y and z |
|
|
|
//------------------------------------- |
|
// dom events generation |
|
//------------------------------------- |
|
firesMouseMoveEventsByDefault: boolean // NOTE: mutually exlusive with firesMouseWheelEventsByDefault |
|
@Writable firesMouseMoveEvents: boolean // NOTE: mutually exlusive with firesMouseWheelEvents |
|
@Writable mouseEventsPointer: PersistentPointer // see later (the pointer for which the wheel events will be generated) |
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
// For a laptop/desktop, this represent a pointer which can be controlled by multiple devices simultaneously (aka the mouse pointer on Windows) |
|
// I envision cases where your app may want to map some devices to another virtual pointer than the 'normal' mouse (for instance, an in-app virtual trackpad to emulate a pen) |
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
class PersistentPointer { |
|
id: int32 // the PointerEvent pointerId associated to events for this virtual pointer |
|
type: string // (usually 'mouse') |
|
screenX: double // the current x position of the pointer relatively to the viewport |
|
screenY: double // the current y position of the pointer relatively to the viewport |
|
//... |
|
} |
|
|
|
} |
|
|
|
interface KeyboardKeyCompatibleInputDevice { |
|
|
|
//-------------------------------- |
|
// device properties (stable) |
|
//-------------------------------- |
|
canFireKeyboardEvents: boolean |
|
|
|
//--------------------------------- |
|
// device state (transient) |
|
//--------------------------------- |
|
isDown: boolean // is currently down or not? (pressure > 0) |
|
pressure: double // from 0.0 to 1.0 where 0.5 is normal |
|
|
|
buttonId: int // a value returned that represent the state/type of the button, if any |
|
buttonIdName: string // a friendly value for the state of the button, if any |
|
// QUESTION: (if all input keys *may* map to one InputDeviceButton (as only one can be reported at the same time by the keyboard); then do those properties change when the button becomes down?) |
|
|
|
//-------------------------------- |
|
// device events |
|
//-------------------------------- |
|
ondown: Function<KeyboardKeyCompatibleInputDeviceEvent> |
|
onup: Function<KeyboardKeyCompatibleInputDeviceEvent> |
|
|
|
//------------------------------- |
|
// dom events generation |
|
//------------------------------- |
|
@Writable firesKeyboardEvents: boolean |
|
|
|
} |
|
|
|
interface MouseButtonCompatibleInputDevice { |
|
|
|
//-------------------------------- |
|
// device properties (stable) |
|
//-------------------------------- |
|
canFireMouseButtonEvents: boolean |
|
|
|
isDown: boolean // is currently down or not? (pressure > 0) |
|
pressure: double // from 0.0 to 1.0 where 0.5 is normal |
|
|
|
//-------------------------------- |
|
// device events |
|
//-------------------------------- |
|
ondown: Function<KeyboardKeyCompatibleInputDeviceEvent> |
|
onup: Function<KeyboardKeyCompatibleInputDeviceEvent> |
|
|
|
//------------------------------- |
|
// dom events generation |
|
//------------------------------- |
|
@Writable firesMouseButtonEvents: boolean |
|
@Writable mouseEventsPointer: PersistentPointer |
|
|
|
} |
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
// An input device which is mostly materialized as a 0D device (though he may support pressure) |
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
class InputDeviceButton extends InputDevicePart { |
|
|
|
name: string // for a mouse "MouseLeft", "MouseMiddle", "MouseRight", ...; for a keyboard: "Ctrl", "Alt", "Shift" or "InputKey" (or ...) |
|
|
|
// a button is supposed to work like a keyboard key for most purposes |
|
@implements KeyboardKeyCompatibleInputDevice; |
|
@implements MouseButtonCompatibleInputDevice; |
|
|
|
} |
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
// An input device which is mostly materialized as a vector in 1D, 2D or 3D space (like a slider, a mouse, or a 3d mouse) |
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
class InputDeviceSlider extends InputDevicePart { |
|
|
|
name: string // "MouseWheel" (or "2Axis-Pad" or "VolumeSlider" or ...) |
|
|
|
//-------------------------------------- |
|
// device events |
|
//-------------------------------------- |
|
onupdated: Function<InputDeviceSliderEvent> // each time the x,y,z values are changed for the device |
|
isDeltaOnlyDevice: boolean // true if the device will be reset to 0 after each change event (like mouses, mouse wheels...) |
|
// NOTE: in this case "onupdated" events will always fire by sequence of events (some value, maybe some updated value, then 0 on reset) |
|
// and any event coming after another update (which is not 0) will actually 'replace' the previously sent value and not add to it |
|
|
|
// This can be a real wheel device or could emulate one easily: |
|
@implements MouseWheelCompatibleInputDevice; |
|
|
|
// This can be a mouse-move wheel device or could emulate one easily once provided a pixelRatio |
|
@implements MouseMoveCompatibleInputDevice |
|
|
|
} |
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
// An input device which is best represented as one or more 2d maps (like a multitouch touchpad or a touchscreen) |
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
class InputDeviceSurface extends InputDevicePart { |
|
|
|
name: string // "Trackpad", "Touchscreen", "Webcam" or ... |
|
|
|
canGengerateBitmapStreams: boolean // can the device output raw bitmaps? |
|
availableBitmapStreams: Array<InputDeviceBitmapSource> // the raw data the surface device can output |
|
|
|
// this could be mapped to an pointing device (touchpad, touchscreen, kinect v2, ...) |
|
@implements ContactCompatibleInputDevice; |
|
|
|
} |
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
// This is like a pointer. It's just exactly what you would expect in a PointerEvent. It's just not a pointer (yet) |
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
class InputDeviceContact { |
|
(see PointerEvent, really, except we talk about device coordinates here and not yet clientX etc (because trackpads are not mapped to the screen for instance)...) |
|
x, y, pressure, hover, angle, ... |
|
} |
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
// Some devices (touchpads, touch mouses, kinects, etc...) can generate a stream of bitmaps which can be used as raw input |
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
class InputDeviceBitmapSource { |
|
|
|
name: string // the name of the source, like "Pressure" or "Depht" or ... |
|
|
|
onupdated: Function<InputDeviceBitmapSourceEvent> |
|
|
|
// for devices that map to the screen like touchscreens or trackballs |
|
isMappedToTheScreen: boolean // true if the following values are known: |
|
xDevicePixelRatio: double // if the bimap is mapped to the screen, by which factor % css pixels (normal zoom settings) |
|
yDevicePixelRatio: double // if the bimap is mapped to the screen, by which factor % css pixels (normal zoom settings) |
|
mappedScreenZone: Rect // relatively to the screens zone, which part of the region is mapped to the device |
|
|
|
} |
Note that Android MotionEvent and X11 XInput API also have some of these properties. Android MotionEvent is, in some sense, even more abstract in that things like 'x', 'y', 'major radius', 'minor radius', 'pressure' are all instances of an 'axis' (which is an enum). Then you can replace a ton of APIs like 'supportsPressure' with 'supportsAxis(AXIS_PRESSURE)'.