globalPointerMoveMonitor.js 3.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. import * as dom from './dom.js';
  6. import { DisposableStore, toDisposable } from '../common/lifecycle.js';
  7. export class GlobalPointerMoveMonitor {
  8. constructor() {
  9. this._hooks = new DisposableStore();
  10. this._pointerMoveCallback = null;
  11. this._onStopCallback = null;
  12. }
  13. dispose() {
  14. this.stopMonitoring(false);
  15. this._hooks.dispose();
  16. }
  17. stopMonitoring(invokeStopCallback, browserEvent) {
  18. if (!this.isMonitoring()) {
  19. // Not monitoring
  20. return;
  21. }
  22. // Unhook
  23. this._hooks.clear();
  24. this._pointerMoveCallback = null;
  25. const onStopCallback = this._onStopCallback;
  26. this._onStopCallback = null;
  27. if (invokeStopCallback && onStopCallback) {
  28. onStopCallback(browserEvent);
  29. }
  30. }
  31. isMonitoring() {
  32. return !!this._pointerMoveCallback;
  33. }
  34. startMonitoring(initialElement, pointerId, initialButtons, pointerMoveCallback, onStopCallback) {
  35. if (this.isMonitoring()) {
  36. this.stopMonitoring(false);
  37. }
  38. this._pointerMoveCallback = pointerMoveCallback;
  39. this._onStopCallback = onStopCallback;
  40. let eventSource = initialElement;
  41. try {
  42. initialElement.setPointerCapture(pointerId);
  43. this._hooks.add(toDisposable(() => {
  44. try {
  45. initialElement.releasePointerCapture(pointerId);
  46. }
  47. catch (err) {
  48. // See https://github.com/microsoft/vscode/issues/161731
  49. //
  50. // `releasePointerCapture` sometimes fails when being invoked with the exception:
  51. // DOMException: Failed to execute 'releasePointerCapture' on 'Element':
  52. // No active pointer with the given id is found.
  53. //
  54. // There's no need to do anything in case of failure
  55. }
  56. }));
  57. }
  58. catch (err) {
  59. // See https://github.com/microsoft/vscode/issues/144584
  60. // See https://github.com/microsoft/vscode/issues/146947
  61. // `setPointerCapture` sometimes fails when being invoked
  62. // from a `mousedown` listener on macOS and Windows
  63. // and it always fails on Linux with the exception:
  64. // DOMException: Failed to execute 'setPointerCapture' on 'Element':
  65. // No active pointer with the given id is found.
  66. // In case of failure, we bind the listeners on the window
  67. eventSource = window;
  68. }
  69. this._hooks.add(dom.addDisposableListener(eventSource, dom.EventType.POINTER_MOVE, (e) => {
  70. if (e.buttons !== initialButtons) {
  71. // Buttons state has changed in the meantime
  72. this.stopMonitoring(true);
  73. return;
  74. }
  75. e.preventDefault();
  76. this._pointerMoveCallback(e);
  77. }));
  78. this._hooks.add(dom.addDisposableListener(eventSource, dom.EventType.POINTER_UP, (e) => this.stopMonitoring(true)));
  79. }
  80. }