• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

excaliburjs / Excalibur / 14840900305

05 May 2025 04:07PM UTC coverage: 87.864% (-1.5%) from 89.319%
14840900305

push

github

web-flow
chore: migrate tests to vitest (#3381)

managed to migrate the engine leak & memory reporters, although it took a bit of rigamarole. Mainly because, as opposed to karma, the reporter runs in node and not the browser. So I have to track the data needed separately in some global hooks _within_ the browser environment, which the reporter then reads and creates the logs if needed.

However, it seems the memory tracking is regularly reporting >1 mb, so I'm not sure if it's tracking properly or if things have changed with the new browsers. My only theory is that because the timing where memory is read is during an `afterEach` _before_ the test's `afterEach`, it's running before any potential cleanup. The timing of this is not something that's easy to control, unfortunately. I did try to prove this theory by doing the memory analysis on the next test's `beforeEach`, but it didnt seem to change the results, so it may not be an issue.

Implementation is done in `src/spec/vitest/__reporters__/memory.ts` and `src/spec/vitest/__reporters/memory.setup.ts`

---

I noticed CouroutineSpec was an offender for >10mb memory, which spins up additional engines. I added a short 100ms wait after the engine.dispose and the memory usage did drop, so it does seem like this is prone to scheduled garbage collecting.

---

I was able to run the garbage collector (if exposed, currently only on chrome) and this makes the reports more accurate

4998 of 6942 branches covered (72.0%)

13655 of 15541 relevant lines covered (87.86%)

25165.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

73.66
/src/engine/Input/PointerEventReceiver.ts
1
import type { Engine } from '../Engine';
2
import { ScrollPreventionMode } from '../Engine';
3
import { GlobalCoordinates } from '../Math/global-coordinates';
4
import type { Vector } from '../Math/vector';
5
import { vec } from '../Math/vector';
6
import { PointerEvent } from './PointerEvent';
7
import { WheelEvent } from './WheelEvent';
8
import { PointerAbstraction } from './PointerAbstraction';
9

10
import { WheelDeltaMode } from './WheelDeltaMode';
11
import { PointerSystem } from './PointerSystem';
12
import { NativePointerButton } from './NativePointerButton';
13
import { PointerButton } from './PointerButton';
14
import { fail } from '../Util/Util';
15
import { PointerType } from './PointerType';
16
import { isCrossOriginIframe } from '../Util/IFrame';
17
import type { EventKey, Handler, Subscription } from '../EventEmitter';
18
import { EventEmitter } from '../EventEmitter';
19

20
export type NativePointerEvent = globalThis.PointerEvent;
21
export type NativeMouseEvent = globalThis.MouseEvent;
22
export type NativeTouchEvent = globalThis.TouchEvent;
23
export type NativeWheelEvent = globalThis.WheelEvent;
24

25
export type PointerEvents = {
26
  move: PointerEvent;
27
  down: PointerEvent;
28
  up: PointerEvent;
29
  wheel: WheelEvent;
30
};
31

32
export const PointerEvents = {
117✔
33
  Move: 'move',
34
  Down: 'down',
35
  Up: 'up',
36
  Wheel: 'wheel'
37
};
38

39
/**
40
 * Is this event a native touch event?
41
 */
42
function isTouchEvent(value: any): value is NativeTouchEvent {
43
  // Guard for Safari <= 13.1
44
  return globalThis.TouchEvent && value instanceof globalThis.TouchEvent;
265✔
45
}
46

47
/**
48
 * Is this event a native pointer event
49
 */
50
function isPointerEvent(value: any): value is NativePointerEvent {
51
  // Guard for Safari <= 13.1
52
  return globalThis.PointerEvent && value instanceof globalThis.PointerEvent;
265✔
53
}
54

55
export interface PointerInitOptions {
56
  grabWindowFocus?: boolean;
57
}
58

59
/**
60
 * The PointerEventProcessor is responsible for collecting all the events from the canvas and transforming them into GlobalCoordinates
61
 */
62
export class PointerEventReceiver {
63
  public events = new EventEmitter<PointerEvents>();
1,403✔
64
  public primary: PointerAbstraction = new PointerAbstraction();
1,403✔
65

66
  private _activeNativePointerIdsToNormalized = new Map<number, number>();
1,403✔
67
  public lastFramePointerCoords = new Map<number, GlobalCoordinates>();
1,403✔
68
  public currentFramePointerCoords = new Map<number, GlobalCoordinates>();
1,403✔
69

70
  public currentFramePointerDown = new Map<number, boolean>();
1,403✔
71
  public lastFramePointerDown = new Map<number, boolean>();
1,403✔
72

73
  public currentFrameDown: PointerEvent[] = [];
1,403✔
74
  public currentFrameUp: PointerEvent[] = [];
1,403✔
75
  public currentFrameMove: PointerEvent[] = [];
1,403✔
76
  public currentFrameCancel: PointerEvent[] = [];
1,403✔
77
  public currentFrameWheel: WheelEvent[] = [];
1,403✔
78

79
  private _enabled = true;
1,403✔
80

81
  constructor(
82
    public readonly target: GlobalEventHandlers & EventTarget,
1,403✔
83
    public engine: Engine
1,403✔
84
  ) {}
85

86
  public toggleEnabled(enabled: boolean) {
87
    this._enabled = enabled;
1,852✔
88
  }
89

90
  /**
91
   * Creates a new PointerEventReceiver with a new target and engine while preserving existing pointer event
92
   * handlers.
93
   * @param target
94
   * @param engine
95
   */
96
  public recreate(target: GlobalEventHandlers & EventTarget, engine: Engine) {
97
    const eventReceiver = new PointerEventReceiver(target, engine);
2✔
98
    eventReceiver.primary = this.primary;
2✔
99
    eventReceiver._pointers = this._pointers;
2✔
100
    return eventReceiver;
2✔
101
  }
102

103
  private _pointers: PointerAbstraction[] = [this.primary];
1,403✔
104
  /**
105
   * Locates a specific pointer by id, creates it if it doesn't exist
106
   * @param index
107
   */
108
  public at(index: number): PointerAbstraction {
109
    if (index >= this._pointers.length) {
89!
110
      // Ensure there is a pointer to retrieve
111
      for (let i = this._pointers.length - 1, max = index; i < max; i++) {
×
112
        this._pointers.push(new PointerAbstraction());
×
113
      }
114
    }
115
    return this._pointers[index];
89✔
116
  }
117

118
  /**
119
   * The number of pointers currently being tracked by excalibur
120
   */
121
  public count(): number {
122
    return this._pointers.length;
×
123
  }
124

125
  /**
126
   * Is the specified pointer id down this frame
127
   * @param pointerId
128
   */
129
  public isDown(pointerId: number) {
130
    if (!this._enabled) {
103!
131
      return false;
×
132
    }
133
    return this.currentFramePointerDown.get(pointerId) ?? false;
103✔
134
  }
135

136
  /**
137
   * Was the specified pointer id down last frame
138
   * @param pointerId
139
   */
140
  public wasDown(pointerId: number) {
141
    if (!this._enabled) {
39!
142
      return false;
×
143
    }
144
    return this.lastFramePointerDown.get(pointerId) ?? false;
39✔
145
  }
146

147
  /**
148
   * Whether the Pointer is currently dragging.
149
   */
150
  public isDragging(pointerId: number): boolean {
151
    if (!this._enabled) {
63!
152
      return false;
×
153
    }
154
    return this.isDown(pointerId);
63✔
155
  }
156

157
  /**
158
   * Whether the Pointer just started dragging.
159
   */
160
  public isDragStart(pointerId: number): boolean {
161
    if (!this._enabled) {
29!
162
      return false;
×
163
    }
164
    return this.isDown(pointerId) && !this.wasDown(pointerId);
29✔
165
  }
166

167
  /**
168
   * Whether the Pointer just ended dragging.
169
   */
170
  public isDragEnd(pointerId: number): boolean {
171
    if (!this._enabled) {
11!
172
      return false;
×
173
    }
174
    return !this.isDown(pointerId) && this.wasDown(pointerId);
11✔
175
  }
176

177
  public emit<TEventName extends EventKey<PointerEvents>>(eventName: TEventName, event: PointerEvents[TEventName]): void;
178
  public emit(eventName: string, event?: any): void;
179
  public emit<TEventName extends EventKey<PointerEvents> | string>(eventName: TEventName, event?: any): void {
180
    this.events.emit(eventName, event);
86✔
181
  }
182

183
  public on<TEventName extends EventKey<PointerEvents>>(eventName: TEventName, handler: Handler<PointerEvents[TEventName]>): Subscription;
184
  public on(eventName: string, handler: Handler<unknown>): Subscription;
185
  public on<TEventName extends EventKey<PointerEvents> | string>(eventName: TEventName, handler: Handler<any>): Subscription {
186
    return this.events.on(eventName, handler);
8✔
187
  }
188

189
  public once<TEventName extends EventKey<PointerEvents>>(eventName: TEventName, handler: Handler<PointerEvents[TEventName]>): Subscription;
190
  public once(eventName: string, handler: Handler<unknown>): Subscription;
191
  public once<TEventName extends EventKey<PointerEvents> | string>(eventName: TEventName, handler: Handler<any>): Subscription {
192
    return this.events.once(eventName, handler);
×
193
  }
194

195
  public off<TEventName extends EventKey<PointerEvents>>(eventName: TEventName, handler: Handler<PointerEvents[TEventName]>): void;
196
  public off(eventName: string, handler: Handler<unknown>): void;
197
  public off(eventName: string): void;
198
  public off<TEventName extends EventKey<PointerEvents> | string>(eventName: TEventName, handler?: Handler<any>): void {
199
    this.events.off(eventName, handler);
×
200
  }
201

202
  /**
203
   * Called internally by excalibur
204
   *
205
   * Updates the current frame pointer info and emits raw pointer events
206
   *
207
   * This does not emit events to entities, see PointerSystem
208
   * @internal
209
   */
210
  public update() {
211
    this.lastFramePointerDown = new Map(this.currentFramePointerDown);
3,628✔
212
    this.lastFramePointerCoords = new Map(this.currentFramePointerCoords);
3,628✔
213

214
    for (const event of this.currentFrameDown) {
3,628✔
215
      if (!event.active) {
47✔
216
        continue;
2✔
217
      }
218
      this.emit('down', event);
45✔
219
      const pointer = this.at(event.pointerId);
45✔
220
      pointer.emit('down', event);
45✔
221
      this.primary.emit('pointerdown', event);
45✔
222
    }
223

224
    for (const event of this.currentFrameUp) {
3,628✔
225
      if (!event.active) {
15✔
226
        continue;
1✔
227
      }
228
      this.emit('up', event);
14✔
229
      const pointer = this.at(event.pointerId);
14✔
230
      pointer.emit('up', event);
14✔
231
    }
232

233
    for (const event of this.currentFrameMove) {
3,628✔
234
      if (!event.active) {
20✔
235
        continue;
1✔
236
      }
237
      this.emit('move', event);
19✔
238
      const pointer = this.at(event.pointerId);
19✔
239
      pointer.emit('move', event);
19✔
240
    }
241

242
    for (const event of this.currentFrameCancel) {
3,628✔
243
      if (!event.active) {
2!
244
        continue;
×
245
      }
246
      this.emit('cancel', event);
2✔
247
      const pointer = this.at(event.pointerId);
2✔
248
      pointer.emit('cancel', event);
2✔
249
    }
250

251
    for (const event of this.currentFrameWheel) {
3,628✔
252
      if (!event.active) {
4✔
253
        continue;
1✔
254
      }
255
      this.emit('pointerwheel', event);
3✔
256
      this.emit('wheel', event);
3✔
257
      this.primary.emit('pointerwheel', event);
3✔
258
      this.primary.emit('wheel', event);
3✔
259
    }
260

261
    if (this.engine.currentScene.camera.hasChanged()) {
3,628✔
262
      for (const pointer of this._pointers) {
1,048✔
263
        pointer._updateWorldPosition(this.engine);
1,048✔
264
      }
265
    }
266
  }
267

268
  /**
269
   * Clears the current frame event and pointer data
270
   */
271
  public clear() {
272
    for (const event of this.currentFrameUp) {
3,664✔
273
      this.currentFramePointerCoords.delete(event.pointerId);
15✔
274
      const ids = this._activeNativePointerIdsToNormalized.entries();
15✔
275
      for (const [native, normalized] of ids) {
15✔
276
        if (normalized === event.pointerId) {
11!
277
          this._activeNativePointerIdsToNormalized.delete(native);
11✔
278
        }
279
      }
280
    }
281
    this.currentFrameDown.length = 0;
3,664✔
282
    this.currentFrameUp.length = 0;
3,664✔
283
    this.currentFrameMove.length = 0;
3,664✔
284
    this.currentFrameCancel.length = 0;
3,664✔
285
    this.currentFrameWheel.length = 0;
3,664✔
286
  }
287

288
  private _boundHandle = this._handle.bind(this);
1,403✔
289
  private _boundWheel = this._handleWheel.bind(this);
1,403✔
290
  /**
291
   * Initializes the pointer event receiver so that it can start listening to native
292
   * browser events.
293
   */
294
  public init(options?: PointerInitOptions) {
295
    if (this.engine.isDisposed()) {
1,403!
296
      return;
×
297
    }
298
    // Disabling the touch action avoids browser/platform gestures from firing on the canvas
299
    // It is important on mobile to have touch action 'none'
300
    // https://stackoverflow.com/questions/48124372/pointermove-event-not-working-with-touch-why-not
301
    if (this.target === this.engine.canvas) {
1,403✔
302
      this.engine.canvas.style.touchAction = 'none';
1,373✔
303
    } else {
304
      document.body.style.touchAction = 'none';
30✔
305
    }
306
    // Preferred pointer events
307
    if (window.PointerEvent) {
1,403!
308
      this.target.addEventListener('pointerdown', this._boundHandle);
1,403✔
309
      this.target.addEventListener('pointerup', this._boundHandle);
1,403✔
310
      this.target.addEventListener('pointermove', this._boundHandle);
1,403✔
311
      this.target.addEventListener('pointercancel', this._boundHandle);
1,403✔
312
    } else {
313
      // Touch Events
314
      this.target.addEventListener('touchstart', this._boundHandle);
×
315
      this.target.addEventListener('touchend', this._boundHandle);
×
316
      this.target.addEventListener('touchmove', this._boundHandle);
×
317
      this.target.addEventListener('touchcancel', this._boundHandle);
×
318

319
      // Mouse Events
320
      this.target.addEventListener('mousedown', this._boundHandle);
×
321
      this.target.addEventListener('mouseup', this._boundHandle);
×
322
      this.target.addEventListener('mousemove', this._boundHandle);
×
323
    }
324

325
    // MDN MouseWheelEvent
326
    const wheelOptions = {
1,403✔
327
      passive: !(
328
        this.engine.pageScrollPreventionMode === ScrollPreventionMode.All ||
2,806✔
329
        this.engine.pageScrollPreventionMode === ScrollPreventionMode.Canvas
330
      )
331
    };
332
    if ('onwheel' in document.createElement('div')) {
1,403!
333
      // Modern Browsers
334
      this.target.addEventListener('wheel', this._boundWheel, wheelOptions);
1,403✔
335
    } else if (document.onmousewheel !== undefined) {
×
336
      // Webkit and IE
337
      this.target.addEventListener('mousewheel', this._boundWheel, wheelOptions);
×
338
    } else {
339
      // Remaining browser and older Firefox
340
      this.target.addEventListener('MozMousePixelScroll', this._boundWheel, wheelOptions);
×
341
    }
342

343
    const grabWindowFocus = options?.grabWindowFocus ?? true;
1,403✔
344
    // Handle cross origin iframe
345
    if (grabWindowFocus && isCrossOriginIframe()) {
1,403!
346
      const grabFocus = () => {
×
347
        window.focus();
×
348
      };
349
      // Preferred pointer events
350
      if (window.PointerEvent) {
×
351
        this.target.addEventListener('pointerdown', grabFocus);
×
352
      } else {
353
        // Touch Events
354
        this.target.addEventListener('touchstart', grabFocus);
×
355

356
        // Mouse Events
357
        this.target.addEventListener('mousedown', grabFocus);
×
358
      }
359
    }
360
  }
361

362
  public detach() {
363
    // Preferred pointer events
364
    if (window.PointerEvent) {
2!
365
      this.target.removeEventListener('pointerdown', this._boundHandle);
2✔
366
      this.target.removeEventListener('pointerup', this._boundHandle);
2✔
367
      this.target.removeEventListener('pointermove', this._boundHandle);
2✔
368
      this.target.removeEventListener('pointercancel', this._boundHandle);
2✔
369
    } else {
370
      // Touch Events
371
      this.target.removeEventListener('touchstart', this._boundHandle);
×
372
      this.target.removeEventListener('touchend', this._boundHandle);
×
373
      this.target.removeEventListener('touchmove', this._boundHandle);
×
374
      this.target.removeEventListener('touchcancel', this._boundHandle);
×
375

376
      // Mouse Events
377
      this.target.removeEventListener('mousedown', this._boundHandle);
×
378
      this.target.removeEventListener('mouseup', this._boundHandle);
×
379
      this.target.removeEventListener('mousemove', this._boundHandle);
×
380
    }
381

382
    if ('onwheel' in document.createElement('div')) {
2!
383
      // Modern Browsers
384
      this.target.removeEventListener('wheel', this._boundWheel);
2✔
385
    } else if (document.onmousewheel !== undefined) {
×
386
      // Webkit and IE
387
      this.target.addEventListener('mousewheel', this._boundWheel);
×
388
    } else {
389
      // Remaining browser and older Firefox
390
      this.target.addEventListener('MozMousePixelScroll', this._boundWheel);
×
391
    }
392
  }
393

394
  /**
395
   * Take native pointer id and map it to index in active pointers
396
   * @param nativePointerId
397
   */
398
  private _normalizePointerId(nativePointerId: number) {
399
    // Add to the the native pointer set id
400
    this._activeNativePointerIdsToNormalized.set(nativePointerId, -1);
265✔
401

402
    // Native pointer ids in ascending order
403
    const currentPointerIds = Array.from(this._activeNativePointerIdsToNormalized.keys()).sort((a, b) => a - b);
265✔
404

405
    // The index into sorted ids will be the new id, will always have an id
406
    const id = currentPointerIds.findIndex((p) => p === nativePointerId);
265✔
407

408
    // Save the mapping so we can reverse it later
409
    this._activeNativePointerIdsToNormalized.set(nativePointerId, id);
265✔
410

411
    // ignore pointer because game isn't watching
412
    return id;
265✔
413
  }
414

415
  /**
416
   * Responsible for handling and parsing pointer events
417
   */
418
  private _handle(ev: NativeTouchEvent | NativePointerEvent | NativeMouseEvent) {
419
    if (!this._enabled) {
446✔
420
      return;
181✔
421
    }
422
    ev.preventDefault();
265✔
423
    const eventCoords = new Map<number, GlobalCoordinates>();
265✔
424
    let button: PointerButton;
425
    let pointerType: PointerType;
426
    if (isTouchEvent(ev)) {
265!
427
      button = PointerButton.Unknown;
×
428
      pointerType = PointerType.Touch;
×
429
      // https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent
430
      for (let i = 0; i < ev.changedTouches.length; i++) {
×
431
        const touch = ev.changedTouches[i];
×
432
        const coordinates = GlobalCoordinates.fromPagePosition(touch.pageX, touch.pageY, this.engine);
×
433
        const nativePointerId = i + 1;
×
434
        const pointerId = this._normalizePointerId(nativePointerId);
×
435
        this.currentFramePointerCoords.set(pointerId, coordinates);
×
436
        eventCoords.set(pointerId, coordinates);
×
437
      }
438
    } else {
439
      button = this._nativeButtonToPointerButton(ev.button);
265✔
440
      pointerType = PointerType.Mouse;
265✔
441
      const coordinates = GlobalCoordinates.fromPagePosition(ev.pageX, ev.pageY, this.engine);
265✔
442
      let nativePointerId = 1;
265✔
443
      if (isPointerEvent(ev)) {
265!
444
        nativePointerId = ev.pointerId;
265✔
445
        pointerType = this._stringToPointerType(ev.pointerType);
265✔
446
      }
447
      const pointerId = this._normalizePointerId(nativePointerId);
265✔
448
      this.currentFramePointerCoords.set(pointerId, coordinates);
265✔
449
      eventCoords.set(pointerId, coordinates);
265✔
450
    }
451

452
    for (const [pointerId, coord] of eventCoords.entries()) {
265✔
453
      switch (ev.type) {
265!
454
        case 'mousedown':
455
        case 'pointerdown':
456
        case 'touchstart':
457
          this.currentFrameDown.push(new PointerEvent('down', pointerId, button, pointerType, coord, ev));
175✔
458
          this.currentFramePointerDown.set(pointerId, true);
175✔
459
          break;
175✔
460
        case 'mouseup':
461
        case 'pointerup':
462
        case 'touchend':
463
          this.currentFrameUp.push(new PointerEvent('up', pointerId, button, pointerType, coord, ev));
38✔
464
          this.currentFramePointerDown.set(pointerId, false);
38✔
465
          break;
38✔
466
        case 'mousemove':
467
        case 'pointermove':
468
        case 'touchmove':
469
          this.currentFrameMove.push(new PointerEvent('move', pointerId, button, pointerType, coord, ev));
50✔
470
          break;
50✔
471
        case 'touchcancel':
472
        case 'pointercancel':
473
          this.currentFrameCancel.push(new PointerEvent('cancel', pointerId, button, pointerType, coord, ev));
2✔
474
          break;
2✔
475
      }
476
    }
477
  }
478

479
  private _handleWheel(ev: NativeWheelEvent) {
480
    if (!this._enabled) {
44✔
481
      return;
20✔
482
    }
483
    // Should we prevent page scroll because of this event
484
    if (
24!
485
      this.engine.pageScrollPreventionMode === ScrollPreventionMode.All ||
72✔
486
      (this.engine.pageScrollPreventionMode === ScrollPreventionMode.Canvas && ev.target === this.engine.canvas)
487
    ) {
488
      ev.preventDefault();
×
489
    }
490
    const screen = this.engine.screen.pageToScreenCoordinates(vec(ev.pageX, ev.pageY));
24✔
491
    const world = this.engine.screen.screenToWorldCoordinates(screen);
24✔
492

493
    /**
494
     * A constant used to normalize wheel events across different browsers
495
     *
496
     * This normalization factor is pulled from
497
     * https://developer.mozilla.org/en-US/docs/Web/Events/wheel#Listening_to_this_event_across_browser
498
     */
499
    const ScrollWheelNormalizationFactor = -1 / 40;
24✔
500

501
    const deltaX = ev.deltaX || ev.wheelDeltaX * ScrollWheelNormalizationFactor || 0;
24✔
502
    const deltaY =
503
      ev.deltaY || ev.wheelDeltaY * ScrollWheelNormalizationFactor || ev.wheelDelta * ScrollWheelNormalizationFactor || ev.detail || 0;
24✔
504
    const deltaZ = ev.deltaZ || 0;
24✔
505
    let deltaMode = WheelDeltaMode.Pixel;
24✔
506

507
    if (ev.deltaMode) {
24!
508
      if (ev.deltaMode === 1) {
×
509
        deltaMode = WheelDeltaMode.Line;
×
510
      } else if (ev.deltaMode === 2) {
×
511
        deltaMode = WheelDeltaMode.Page;
×
512
      }
513
    }
514

515
    const we = new WheelEvent(world.x, world.y, ev.pageX, ev.pageY, screen.x, screen.y, 0, deltaX, deltaY, deltaZ, deltaMode, ev);
24✔
516
    this.currentFrameWheel.push(we);
24✔
517
  }
518

519
  /**
520
   * Triggers an excalibur pointer event in a world space pos
521
   *
522
   * Useful for testing pointers in excalibur
523
   * @param type
524
   * @param pos
525
   */
526
  public triggerEvent(type: 'down' | 'up' | 'move' | 'cancel', pos: Vector) {
527
    const page = this.engine.screen.worldToPageCoordinates(pos);
26✔
528
    // Send an event to the event receiver
529
    if (window.PointerEvent) {
26!
530
      this._handle(
26✔
531
        new window.PointerEvent('pointer' + type, {
532
          pointerId: 0,
533
          clientX: page.x,
534
          clientY: page.y
535
        })
536
      );
537
    } else {
538
      // Safari hack
539
      this._handle(
×
540
        new window.MouseEvent('mouse' + type, {
541
          clientX: page.x,
542
          clientY: page.y
543
        })
544
      );
545
    }
546

547
    // Force update pointer system
548
    const pointerSystem = this.engine.currentScene.world.get(PointerSystem);
26✔
549
    pointerSystem.preupdate(this.engine.currentScene, 1);
26✔
550
    pointerSystem.update(1);
26✔
551
  }
552

553
  private _nativeButtonToPointerButton(s: NativePointerButton): PointerButton {
554
    switch (s) {
265!
555
      case NativePointerButton.NoButton:
556
        return PointerButton.NoButton;
×
557
      case NativePointerButton.Left:
558
        return PointerButton.Left;
251✔
559
      case NativePointerButton.Middle:
560
        return PointerButton.Middle;
7✔
561
      case NativePointerButton.Right:
562
        return PointerButton.Right;
7✔
563
      case NativePointerButton.Unknown:
564
        return PointerButton.Unknown;
×
565
      default:
566
        return fail(s);
×
567
    }
568
  }
569

570
  private _stringToPointerType(s: string) {
571
    switch (s) {
265!
572
      case 'touch':
573
        return PointerType.Touch;
×
574
      case 'mouse':
575
        return PointerType.Mouse;
×
576
      case 'pen':
577
        return PointerType.Pen;
×
578
      default:
579
        return PointerType.Unknown;
265✔
580
    }
581
  }
582
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc