@xterm/xterm 6.1.0-beta.28 → 6.1.0-beta.280

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. package/README.md +62 -38
  2. package/css/xterm.css +29 -22
  3. package/lib/xterm.js +1 -1
  4. package/lib/xterm.js.map +1 -1
  5. package/lib/xterm.mjs +8 -34
  6. package/lib/xterm.mjs.map +4 -4
  7. package/package.json +56 -45
  8. package/src/browser/AccessibilityManager.ts +18 -13
  9. package/src/browser/Clipboard.ts +8 -5
  10. package/src/browser/ColorContrastCache.ts +3 -3
  11. package/src/browser/CoreBrowserTerminal.ts +179 -348
  12. package/src/browser/Dom.ts +178 -0
  13. package/src/browser/Linkifier.ts +14 -14
  14. package/src/browser/OscLinkProvider.ts +86 -18
  15. package/src/browser/RenderDebouncer.ts +7 -9
  16. package/src/browser/TimeBasedDebouncer.ts +12 -5
  17. package/src/browser/Types.ts +16 -14
  18. package/src/browser/Viewport.ts +58 -23
  19. package/src/browser/decorations/BufferDecorationRenderer.ts +3 -3
  20. package/src/browser/decorations/ColorZoneStore.ts +1 -1
  21. package/src/browser/decorations/OverviewRulerRenderer.ts +36 -20
  22. package/src/browser/input/CompositionHelper.ts +47 -11
  23. package/src/browser/input/Mouse.ts +5 -9
  24. package/src/browser/input/MoveToCell.ts +3 -3
  25. package/src/browser/public/Terminal.ts +33 -36
  26. package/src/browser/renderer/dom/DomRenderer.ts +265 -89
  27. package/src/browser/renderer/dom/DomRendererRowFactory.ts +34 -27
  28. package/src/browser/renderer/dom/WidthCache.ts +57 -55
  29. package/src/browser/renderer/shared/Constants.ts +7 -0
  30. package/src/browser/renderer/shared/RendererUtils.ts +1 -1
  31. package/src/browser/renderer/shared/SelectionRenderModel.ts +2 -2
  32. package/src/browser/renderer/shared/TextBlinkStateManager.ts +97 -0
  33. package/src/browser/renderer/shared/Types.ts +10 -4
  34. package/src/browser/scrollable/abstractScrollbar.ts +300 -0
  35. package/src/browser/scrollable/fastDomNode.ts +126 -0
  36. package/src/browser/scrollable/globalPointerMoveMonitor.ts +90 -0
  37. package/src/browser/scrollable/horizontalScrollbar.ts +85 -0
  38. package/src/browser/scrollable/mouseEvent.ts +292 -0
  39. package/src/browser/scrollable/scrollable.ts +486 -0
  40. package/src/browser/scrollable/scrollableElement.ts +581 -0
  41. package/src/browser/scrollable/scrollableElementOptions.ts +161 -0
  42. package/src/browser/scrollable/scrollbarArrow.ts +110 -0
  43. package/src/browser/scrollable/scrollbarState.ts +246 -0
  44. package/src/browser/scrollable/scrollbarVisibilityController.ts +113 -0
  45. package/src/browser/scrollable/touch.ts +485 -0
  46. package/src/browser/scrollable/verticalScrollbar.ts +143 -0
  47. package/src/browser/scrollable/widget.ts +23 -0
  48. package/src/browser/selection/SelectionModel.ts +1 -1
  49. package/src/browser/services/CharSizeService.ts +4 -4
  50. package/src/browser/services/CharacterJoinerService.ts +13 -11
  51. package/src/browser/services/CoreBrowserService.ts +7 -5
  52. package/src/browser/services/KeyboardService.ts +67 -0
  53. package/src/browser/services/LinkProviderService.ts +3 -3
  54. package/src/browser/services/MouseCoordsService.ts +47 -0
  55. package/src/browser/services/MouseService.ts +619 -23
  56. package/src/browser/services/RenderService.ts +33 -21
  57. package/src/browser/services/SelectionService.ts +72 -54
  58. package/src/browser/services/Services.ts +44 -21
  59. package/src/browser/services/ThemeService.ts +9 -9
  60. package/src/common/Async.ts +141 -0
  61. package/src/common/CircularList.ts +24 -3
  62. package/src/common/Color.ts +13 -5
  63. package/src/common/CoreTerminal.ts +61 -35
  64. package/src/common/Event.ts +118 -0
  65. package/src/common/InputHandler.ts +286 -105
  66. package/src/common/Lifecycle.ts +113 -0
  67. package/src/common/Platform.ts +15 -5
  68. package/src/common/SortedList.ts +8 -4
  69. package/src/common/StringBuilder.ts +67 -0
  70. package/src/common/TaskQueue.ts +17 -8
  71. package/src/common/Types.ts +68 -206
  72. package/src/common/Version.ts +1 -1
  73. package/src/common/WindowsMode.ts +2 -2
  74. package/src/common/buffer/AttributeData.ts +3 -2
  75. package/src/common/buffer/Buffer.ts +47 -32
  76. package/src/common/buffer/BufferLine.ts +175 -100
  77. package/src/common/buffer/BufferLineStringCache.ts +69 -0
  78. package/src/common/buffer/BufferReflow.ts +7 -4
  79. package/src/common/buffer/BufferSet.ts +13 -9
  80. package/src/common/buffer/CellData.ts +61 -4
  81. package/src/common/buffer/Constants.ts +13 -13
  82. package/src/common/buffer/Marker.ts +4 -4
  83. package/src/common/buffer/Types.ts +153 -3
  84. package/src/common/data/Charsets.ts +4 -7
  85. package/src/common/data/EscapeSequences.ts +71 -70
  86. package/src/common/input/Keyboard.ts +17 -10
  87. package/src/common/input/KittyKeyboard.ts +526 -0
  88. package/src/common/input/TextDecoder.ts +3 -3
  89. package/src/common/input/UnicodeV6.ts +3 -2
  90. package/src/common/input/Win32InputMode.ts +297 -0
  91. package/src/common/input/WriteBuffer.ts +108 -39
  92. package/src/common/input/XParseColor.ts +2 -2
  93. package/src/common/parser/ApcParser.ts +196 -0
  94. package/src/common/parser/Constants.ts +14 -4
  95. package/src/common/parser/DcsParser.ts +15 -16
  96. package/src/common/parser/EscapeSequenceParser.ts +214 -72
  97. package/src/common/parser/OscParser.ts +14 -15
  98. package/src/common/parser/Params.ts +31 -12
  99. package/src/common/parser/Types.ts +40 -30
  100. package/src/common/public/BufferApiView.ts +3 -3
  101. package/src/common/public/BufferLineApiView.ts +4 -4
  102. package/src/common/public/BufferNamespaceApi.ts +5 -5
  103. package/src/common/public/ParserApi.ts +5 -2
  104. package/src/common/public/UnicodeApi.ts +1 -1
  105. package/src/common/services/BufferService.ts +17 -13
  106. package/src/common/services/CharsetService.ts +6 -2
  107. package/src/common/services/CoreService.ts +23 -10
  108. package/src/common/services/DecorationService.ts +260 -15
  109. package/src/common/services/InstantiationService.ts +2 -2
  110. package/src/common/services/LogService.ts +2 -32
  111. package/src/common/services/{CoreMouseService.ts → MouseStateService.ts} +22 -133
  112. package/src/common/services/OptionsService.ts +17 -7
  113. package/src/common/services/OscLinkService.ts +3 -2
  114. package/src/common/services/ServiceRegistry.ts +14 -8
  115. package/src/common/services/Services.ts +54 -50
  116. package/src/common/services/UnicodeService.ts +6 -11
  117. package/typings/xterm.d.ts +329 -34
  118. package/src/common/Clone.ts +0 -23
  119. package/src/common/TypedArrayUtils.ts +0 -17
  120. package/src/vs/base/browser/browser.ts +0 -141
  121. package/src/vs/base/browser/canIUse.ts +0 -49
  122. package/src/vs/base/browser/dom.ts +0 -2369
  123. package/src/vs/base/browser/fastDomNode.ts +0 -316
  124. package/src/vs/base/browser/globalPointerMoveMonitor.ts +0 -112
  125. package/src/vs/base/browser/iframe.ts +0 -135
  126. package/src/vs/base/browser/keyboardEvent.ts +0 -213
  127. package/src/vs/base/browser/mouseEvent.ts +0 -229
  128. package/src/vs/base/browser/touch.ts +0 -372
  129. package/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts +0 -303
  130. package/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts +0 -114
  131. package/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +0 -720
  132. package/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts +0 -165
  133. package/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts +0 -114
  134. package/src/vs/base/browser/ui/scrollbar/scrollbarState.ts +0 -243
  135. package/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts +0 -118
  136. package/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts +0 -116
  137. package/src/vs/base/browser/ui/widget.ts +0 -57
  138. package/src/vs/base/browser/window.ts +0 -14
  139. package/src/vs/base/common/arrays.ts +0 -887
  140. package/src/vs/base/common/arraysFind.ts +0 -202
  141. package/src/vs/base/common/assert.ts +0 -71
  142. package/src/vs/base/common/async.ts +0 -1992
  143. package/src/vs/base/common/cancellation.ts +0 -148
  144. package/src/vs/base/common/charCode.ts +0 -450
  145. package/src/vs/base/common/collections.ts +0 -140
  146. package/src/vs/base/common/decorators.ts +0 -130
  147. package/src/vs/base/common/equals.ts +0 -146
  148. package/src/vs/base/common/errors.ts +0 -303
  149. package/src/vs/base/common/event.ts +0 -1778
  150. package/src/vs/base/common/functional.ts +0 -32
  151. package/src/vs/base/common/hash.ts +0 -316
  152. package/src/vs/base/common/iterator.ts +0 -159
  153. package/src/vs/base/common/keyCodes.ts +0 -526
  154. package/src/vs/base/common/keybindings.ts +0 -284
  155. package/src/vs/base/common/lazy.ts +0 -47
  156. package/src/vs/base/common/lifecycle.ts +0 -801
  157. package/src/vs/base/common/linkedList.ts +0 -142
  158. package/src/vs/base/common/map.ts +0 -202
  159. package/src/vs/base/common/numbers.ts +0 -98
  160. package/src/vs/base/common/observable.ts +0 -76
  161. package/src/vs/base/common/observableInternal/api.ts +0 -31
  162. package/src/vs/base/common/observableInternal/autorun.ts +0 -281
  163. package/src/vs/base/common/observableInternal/base.ts +0 -489
  164. package/src/vs/base/common/observableInternal/debugName.ts +0 -145
  165. package/src/vs/base/common/observableInternal/derived.ts +0 -428
  166. package/src/vs/base/common/observableInternal/lazyObservableValue.ts +0 -146
  167. package/src/vs/base/common/observableInternal/logging.ts +0 -328
  168. package/src/vs/base/common/observableInternal/promise.ts +0 -209
  169. package/src/vs/base/common/observableInternal/utils.ts +0 -610
  170. package/src/vs/base/common/platform.ts +0 -281
  171. package/src/vs/base/common/scrollable.ts +0 -522
  172. package/src/vs/base/common/sequence.ts +0 -34
  173. package/src/vs/base/common/stopwatch.ts +0 -43
  174. package/src/vs/base/common/strings.ts +0 -557
  175. package/src/vs/base/common/symbols.ts +0 -9
  176. package/src/vs/base/common/uint.ts +0 -59
  177. package/src/vs/patches/nls.ts +0 -90
  178. package/src/vs/typings/base-common.d.ts +0 -20
  179. package/src/vs/typings/require.d.ts +0 -42
  180. package/src/vs/typings/vscode-globals-nls.d.ts +0 -36
  181. package/src/vs/typings/vscode-globals-product.d.ts +0 -33
@@ -0,0 +1,297 @@
1
+ /**
2
+ * Copyright (c) 2026 The xterm.js authors. All rights reserved.
3
+ * @license MIT
4
+ *
5
+ * Win32 input mode implementation.
6
+ * @see https://github.com/microsoft/terminal/blob/main/doc/specs/%234999%20-%20Improved%20keyboard%20handling%20in%20Conpty.md
7
+ *
8
+ * Format: CSI Vk ; Sc ; Uc ; Kd ; Cs ; Rc _
9
+ * Vk: Virtual key code (decimal)
10
+ * Sc: Scan code (decimal)
11
+ * Uc: Unicode character (decimal codepoint, 0 if none)
12
+ * Kd: Key down (1) or up (0)
13
+ * Cs: Control key state (modifier flags)
14
+ * Rc: Repeat count (usually 1)
15
+ */
16
+
17
+ import { IKeyboardEvent, IKeyboardResult, KeyboardResultType } from '../Types';
18
+ import { C0 } from '../data/EscapeSequences';
19
+
20
+ /**
21
+ * Win32 control key state flags (from Windows API).
22
+ */
23
+ export const enum Win32ControlKeyState {
24
+ RIGHT_ALT_PRESSED = 0b000000001,
25
+ LEFT_ALT_PRESSED = 0b000000010,
26
+ RIGHT_CTRL_PRESSED = 0b000000100,
27
+ LEFT_CTRL_PRESSED = 0b000001000,
28
+ SHIFT_PRESSED = 0b000010000,
29
+ NUMLOCK_ON = 0b000100000,
30
+ SCROLLLOCK_ON = 0b001000000,
31
+ CAPSLOCK_ON = 0b010000000,
32
+ ENHANCED_KEY = 0b100000000,
33
+ }
34
+
35
+ /**
36
+ * Win32 input mode handler. Lookup tables are only initialized when this class
37
+ * is instantiated, reducing bundle size for environments that don't use this mode.
38
+ */
39
+ export class Win32InputMode {
40
+ /**
41
+ * Mapping from browser KeyboardEvent.code to Win32 virtual key codes.
42
+ * Based on https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
43
+ */
44
+ private readonly _codeToVk: { [code: string]: number } = {
45
+ // Letters
46
+ 'KeyA': 0x41, 'KeyB': 0x42, 'KeyC': 0x43, 'KeyD': 0x44, 'KeyE': 0x45,
47
+ 'KeyF': 0x46, 'KeyG': 0x47, 'KeyH': 0x48, 'KeyI': 0x49, 'KeyJ': 0x4A,
48
+ 'KeyK': 0x4B, 'KeyL': 0x4C, 'KeyM': 0x4D, 'KeyN': 0x4E, 'KeyO': 0x4F,
49
+ 'KeyP': 0x50, 'KeyQ': 0x51, 'KeyR': 0x52, 'KeyS': 0x53, 'KeyT': 0x54,
50
+ 'KeyU': 0x55, 'KeyV': 0x56, 'KeyW': 0x57, 'KeyX': 0x58, 'KeyY': 0x59,
51
+ 'KeyZ': 0x5A,
52
+
53
+ // Digits
54
+ 'Digit0': 0x30, 'Digit1': 0x31, 'Digit2': 0x32, 'Digit3': 0x33, 'Digit4': 0x34,
55
+ 'Digit5': 0x35, 'Digit6': 0x36, 'Digit7': 0x37, 'Digit8': 0x38, 'Digit9': 0x39,
56
+
57
+ // Function keys
58
+ 'F1': 0x70, 'F2': 0x71, 'F3': 0x72, 'F4': 0x73, 'F5': 0x74, 'F6': 0x75,
59
+ 'F7': 0x76, 'F8': 0x77, 'F9': 0x78, 'F10': 0x79, 'F11': 0x7A, 'F12': 0x7B,
60
+ 'F13': 0x7C, 'F14': 0x7D, 'F15': 0x7E, 'F16': 0x7F, 'F17': 0x80, 'F18': 0x81,
61
+ 'F19': 0x82, 'F20': 0x83, 'F21': 0x84, 'F22': 0x85, 'F23': 0x86, 'F24': 0x87,
62
+
63
+ // Numpad
64
+ 'Numpad0': 0x60, 'Numpad1': 0x61, 'Numpad2': 0x62, 'Numpad3': 0x63, 'Numpad4': 0x64,
65
+ 'Numpad5': 0x65, 'Numpad6': 0x66, 'Numpad7': 0x67, 'Numpad8': 0x68, 'Numpad9': 0x69,
66
+ 'NumpadMultiply': 0x6A, 'NumpadAdd': 0x6B, 'NumpadSeparator': 0x6C,
67
+ 'NumpadSubtract': 0x6D, 'NumpadDecimal': 0x6E, 'NumpadDivide': 0x6F,
68
+ 'NumpadEnter': 0x0D, // Same as Enter but with ENHANCED_KEY flag
69
+ 'NumLock': 0x90,
70
+
71
+ // Navigation
72
+ 'ArrowUp': 0x26, 'ArrowDown': 0x28, 'ArrowLeft': 0x25, 'ArrowRight': 0x27,
73
+ 'Home': 0x24, 'End': 0x23, 'PageUp': 0x21, 'PageDown': 0x22,
74
+ 'Insert': 0x2D, 'Delete': 0x2E,
75
+
76
+ // Modifiers
77
+ 'ShiftLeft': 0x10, 'ShiftRight': 0x10,
78
+ 'ControlLeft': 0x11, 'ControlRight': 0x11,
79
+ 'AltLeft': 0x12, 'AltRight': 0x12,
80
+ 'MetaLeft': 0x5B, 'MetaRight': 0x5C,
81
+ 'CapsLock': 0x14, 'ScrollLock': 0x91,
82
+
83
+ // Special keys
84
+ 'Escape': 0x1B, 'Enter': 0x0D, 'Tab': 0x09, 'Space': 0x20,
85
+ 'Backspace': 0x08, 'Pause': 0x13, 'ContextMenu': 0x5D, 'PrintScreen': 0x2C,
86
+
87
+ // OEM keys (US keyboard layout)
88
+ 'Semicolon': 0xBA, // ;:
89
+ 'Equal': 0xBB, // =+
90
+ 'Comma': 0xBC, // ,<
91
+ 'Minus': 0xBD, // -_
92
+ 'Period': 0xBE, // .>
93
+ 'Slash': 0xBF, // /?
94
+ 'Backquote': 0xC0, // `~
95
+ 'BracketLeft': 0xDB, // [{
96
+ 'Backslash': 0xDC, // \|
97
+ 'BracketRight': 0xDD, // ]}
98
+ 'Quote': 0xDE, // '"
99
+ 'IntlBackslash': 0xE2 // Non-US backslash
100
+ };
101
+
102
+ /**
103
+ * Mapping from browser KeyboardEvent.code to approximate Win32 scan codes.
104
+ * Note: Scan codes can vary by keyboard layout. These are approximations
105
+ * based on standard US keyboard layout.
106
+ */
107
+ private readonly _codeToScancode: { [code: string]: number } = {
108
+ // Letters (row by row)
109
+ 'KeyQ': 0x10, 'KeyW': 0x11, 'KeyE': 0x12, 'KeyR': 0x13, 'KeyT': 0x14,
110
+ 'KeyY': 0x15, 'KeyU': 0x16, 'KeyI': 0x17, 'KeyO': 0x18, 'KeyP': 0x19,
111
+ 'KeyA': 0x1E, 'KeyS': 0x1F, 'KeyD': 0x20, 'KeyF': 0x21, 'KeyG': 0x22,
112
+ 'KeyH': 0x23, 'KeyJ': 0x24, 'KeyK': 0x25, 'KeyL': 0x26,
113
+ 'KeyZ': 0x2C, 'KeyX': 0x2D, 'KeyC': 0x2E, 'KeyV': 0x2F, 'KeyB': 0x30,
114
+ 'KeyN': 0x31, 'KeyM': 0x32,
115
+
116
+ // Digits
117
+ 'Digit1': 0x02, 'Digit2': 0x03, 'Digit3': 0x04, 'Digit4': 0x05, 'Digit5': 0x06,
118
+ 'Digit6': 0x07, 'Digit7': 0x08, 'Digit8': 0x09, 'Digit9': 0x0A, 'Digit0': 0x0B,
119
+
120
+ // Function keys
121
+ 'F1': 0x3B, 'F2': 0x3C, 'F3': 0x3D, 'F4': 0x3E, 'F5': 0x3F, 'F6': 0x40,
122
+ 'F7': 0x41, 'F8': 0x42, 'F9': 0x43, 'F10': 0x44, 'F11': 0x57, 'F12': 0x58,
123
+
124
+ // Numpad
125
+ 'Numpad0': 0x52, 'Numpad1': 0x4F, 'Numpad2': 0x50, 'Numpad3': 0x51, 'Numpad4': 0x4B,
126
+ 'Numpad5': 0x4C, 'Numpad6': 0x4D, 'Numpad7': 0x47, 'Numpad8': 0x48, 'Numpad9': 0x49,
127
+ 'NumpadMultiply': 0x37, 'NumpadAdd': 0x4E, 'NumpadSubtract': 0x4A,
128
+ 'NumpadDecimal': 0x53, 'NumpadDivide': 0x35, 'NumpadEnter': 0x1C,
129
+ 'NumLock': 0x45,
130
+
131
+ // Navigation (extended keys)
132
+ 'ArrowUp': 0x48, 'ArrowDown': 0x50, 'ArrowLeft': 0x4B, 'ArrowRight': 0x4D,
133
+ 'Home': 0x47, 'End': 0x4F, 'PageUp': 0x49, 'PageDown': 0x51,
134
+ 'Insert': 0x52, 'Delete': 0x53,
135
+
136
+ // Modifiers
137
+ 'ShiftLeft': 0x2A, 'ShiftRight': 0x36,
138
+ 'ControlLeft': 0x1D, 'ControlRight': 0x1D,
139
+ 'AltLeft': 0x38, 'AltRight': 0x38,
140
+ 'CapsLock': 0x3A, 'ScrollLock': 0x46,
141
+
142
+ // Special keys
143
+ 'Escape': 0x01, 'Enter': 0x1C, 'Tab': 0x0F, 'Space': 0x39,
144
+ 'Backspace': 0x0E, 'Pause': 0x45,
145
+
146
+ // OEM keys
147
+ 'Semicolon': 0x27, 'Equal': 0x0D, 'Comma': 0x33, 'Minus': 0x0C,
148
+ 'Period': 0x34, 'Slash': 0x35, 'Backquote': 0x29,
149
+ 'BracketLeft': 0x1A, 'Backslash': 0x2B, 'BracketRight': 0x1B, 'Quote': 0x28
150
+ };
151
+
152
+ /**
153
+ * Codes that represent enhanced keys (extended keyboard keys).
154
+ */
155
+ private readonly _enhancedKeyCodes = new Set([
156
+ 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight',
157
+ 'Home', 'End', 'PageUp', 'PageDown', 'Insert', 'Delete',
158
+ 'NumpadEnter', 'NumpadDivide',
159
+ 'ControlRight', 'AltRight',
160
+ 'PrintScreen', 'Pause', 'ContextMenu',
161
+ 'MetaLeft', 'MetaRight'
162
+ ]);
163
+
164
+ /**
165
+ * Mapping of special keys (ev.key values) to their Unicode control character codes.
166
+ * These keys have multi-character ev.key strings but produce control characters.
167
+ * @see https://docs.microsoft.com/en-us/windows/console/key-event-record-str
168
+ */
169
+ private readonly _keyToControlChar: { [key: string]: number } = {
170
+ 'Enter': 0x0D, // Carriage return
171
+ 'Backspace': 0x08, // Backspace
172
+ 'Tab': 0x09, // Horizontal tab
173
+ 'Escape': 0x1B // Escape
174
+ };
175
+
176
+ /**
177
+ * Get the Win32 virtual key code for a keyboard event.
178
+ */
179
+ private _getVirtualKeyCode(ev: IKeyboardEvent): number {
180
+ const vk = this._codeToVk[ev.code];
181
+ if (vk !== undefined) {
182
+ return vk;
183
+ }
184
+ // Fall back to keyCode for unmapped keys
185
+ return ev.keyCode || 0;
186
+ }
187
+
188
+ /**
189
+ * Get the Win32 scan code for a keyboard event.
190
+ * Returns 0 if unknown (scan codes vary by hardware).
191
+ */
192
+ private _getScanCode(ev: IKeyboardEvent): number {
193
+ return this._codeToScancode[ev.code] || 0;
194
+ }
195
+
196
+ /**
197
+ * Get the unicode character for a keyboard event.
198
+ * Returns 0 for non-character keys.
199
+ */
200
+ private _getUnicodeChar(ev: IKeyboardEvent): number {
201
+ // Handle special keys that produce control characters
202
+ // Ctrl modifies some of these: Ctrl+Enter=LF, Ctrl+Backspace=DEL
203
+ if (ev.ctrlKey && !ev.altKey && !ev.metaKey) {
204
+ if (ev.key === 'Enter') {
205
+ return 0x0A; // Line feed (Ctrl+Enter)
206
+ }
207
+ if (ev.key === 'Backspace') {
208
+ return 0x7F; // DEL (Ctrl+Backspace)
209
+ }
210
+ }
211
+
212
+ // Check for special keys that always produce control characters
213
+ const controlChar = this._keyToControlChar[ev.key];
214
+ if (controlChar !== undefined) {
215
+ return controlChar;
216
+ }
217
+
218
+ // Only single-character keys produce unicode output
219
+ if (ev.key.length === 1) {
220
+ const codePoint = ev.key.codePointAt(0) || 0;
221
+
222
+ // Handle Ctrl+letter combinations - these produce control characters (0x01-0x1A)
223
+ if (ev.ctrlKey && !ev.altKey && !ev.metaKey) {
224
+ // Convert A-Z or a-z to control character (Ctrl+A = 0x01, Ctrl+C = 0x03, etc.)
225
+ if (codePoint >= 0x41 && codePoint <= 0x5A) { // A-Z
226
+ return codePoint - 0x40;
227
+ }
228
+ if (codePoint >= 0x61 && codePoint <= 0x7A) { // a-z
229
+ return codePoint - 0x60;
230
+ }
231
+ }
232
+
233
+ return codePoint;
234
+ }
235
+ return 0;
236
+ }
237
+
238
+ /**
239
+ * Get the Win32 control key state flags.
240
+ */
241
+ private _getControlKeyState(ev: IKeyboardEvent): number {
242
+ let state = 0;
243
+
244
+ if (ev.shiftKey) {
245
+ state |= Win32ControlKeyState.SHIFT_PRESSED;
246
+ }
247
+
248
+ // Note: We can't distinguish left/right for ctrl/alt in standard browser events,
249
+ // so we use the generic pressed flags. The right-side flags are used when
250
+ // we can detect them (e.g., via code property).
251
+ if (ev.ctrlKey) {
252
+ if (ev.code === 'ControlRight') {
253
+ state |= Win32ControlKeyState.RIGHT_CTRL_PRESSED;
254
+ } else {
255
+ state |= Win32ControlKeyState.LEFT_CTRL_PRESSED;
256
+ }
257
+ }
258
+
259
+ if (ev.altKey) {
260
+ if (ev.code === 'AltRight') {
261
+ state |= Win32ControlKeyState.RIGHT_ALT_PRESSED;
262
+ } else {
263
+ state |= Win32ControlKeyState.LEFT_ALT_PRESSED;
264
+ }
265
+ }
266
+
267
+ // Check for enhanced key
268
+ if (this._enhancedKeyCodes.has(ev.code)) {
269
+ state |= Win32ControlKeyState.ENHANCED_KEY;
270
+ }
271
+
272
+ return state;
273
+ }
274
+
275
+ /**
276
+ * Evaluate a keyboard event using Win32 input mode.
277
+ *
278
+ * @param ev The keyboard event.
279
+ * @param isKeyDown Whether this is a keydown (true) or keyup (false) event.
280
+ * @returns The keyboard result with the encoded key sequence.
281
+ */
282
+ public evaluateKeyboardEvent(ev: IKeyboardEvent, isKeyDown: boolean): IKeyboardResult {
283
+ const vk = this._getVirtualKeyCode(ev);
284
+ const sc = this._getScanCode(ev);
285
+ const uc = this._getUnicodeChar(ev);
286
+ const kd = isKeyDown ? 1 : 0;
287
+ const cs = this._getControlKeyState(ev);
288
+ const rc = 1; // Repeat count, always 1 for now
289
+
290
+ // Format: CSI Vk ; Sc ; Uc ; Kd ; Cs ; Rc _
291
+ return {
292
+ type: KeyboardResultType.SEND_KEY,
293
+ cancel: true,
294
+ key: `${C0.ESC}[${vk};${sc};${uc};${kd};${cs};${rc}_`
295
+ };
296
+ }
297
+ }
@@ -4,35 +4,34 @@
4
4
  * @license MIT
5
5
  */
6
6
 
7
- import { Disposable } from 'vs/base/common/lifecycle';
8
- import { Emitter } from 'vs/base/common/event';
7
+ import { TimeoutTimer } from '../Async';
8
+ import { Disposable, toDisposable } from '../Lifecycle';
9
+ import { Emitter } from '../Event';
9
10
 
10
- declare const setTimeout: (handler: () => void, timeout?: number) => void;
11
-
12
- /**
13
- * Safety watermark to avoid memory exhaustion and browser engine crash on fast data input.
14
- * Enable flow control to avoid this limit and make sure that your backend correctly
15
- * propagates this to the underlying pty. (see docs for further instructions)
16
- * Since this limit is meant as a safety parachute to prevent browser crashs,
17
- * it is set to a very high number. Typically xterm.js gets unresponsive with
18
- * a 100 times lower number (>500 kB).
19
- */
20
- const DISCARD_WATERMARK = 50000000; // ~50 MB
21
-
22
- /**
23
- * The max number of ms to spend on writes before allowing the renderer to
24
- * catch up with a 0ms setTimeout. A value of < 33 to keep us close to
25
- * 30fps, and a value of < 16 to try to run at 60fps. Of course, the real FPS
26
- * depends on the time it takes for the renderer to draw the frame.
27
- */
28
- const WRITE_TIMEOUT_MS = 12;
29
-
30
- /**
31
- * Threshold of max held chunks in the write buffer, that were already processed.
32
- * This is a tradeoff between extensive write buffer shifts (bad runtime) and high
33
- * memory consumption by data thats not used anymore.
34
- */
35
- const WRITE_BUFFER_LENGTH_THRESHOLD = 50;
11
+ const enum Constants {
12
+ /**
13
+ * Safety watermark to avoid memory exhaustion and browser engine crash on fast data input.
14
+ * Enable flow control to avoid this limit and make sure that your backend correctly
15
+ * propagates this to the underlying pty. (see docs for further instructions)
16
+ * Since this limit is meant as a safety parachute to prevent browser crashs,
17
+ * it is set to a very high number. Typically xterm.js gets unresponsive with
18
+ * a 100 times lower number (>500 kB).
19
+ */
20
+ DISCARD_WATERMARK = 50000000, // ~50 MB
21
+ /**
22
+ * The max number of ms to spend on writes before allowing the renderer to
23
+ * catch up with a 0ms setTimeout. A value of < 33 to keep us close to
24
+ * 30fps, and a value of < 16 to try to run at 60fps. Of course, the real FPS
25
+ * depends on the time it takes for the renderer to draw the frame.
26
+ */
27
+ WRITE_TIMEOUT_MS = 12,
28
+ /**
29
+ * Threshold of max held chunks in the write buffer, that were already processed.
30
+ * This is a tradeoff between extensive write buffer shifts (bad runtime) and high
31
+ * memory consumption by data thats not used anymore.
32
+ */
33
+ WRITE_BUFFER_LENGTH_THRESHOLD = 50
34
+ }
36
35
 
37
36
  export class WriteBuffer extends Disposable {
38
37
  private _writeBuffer: (string | Uint8Array)[] = [];
@@ -43,21 +42,71 @@ export class WriteBuffer extends Disposable {
43
42
  private _syncCalls = 0;
44
43
  private _didUserInput = false;
45
44
 
45
+ private readonly _innerWriteTimer = this._register(new TimeoutTimer());
46
46
  private readonly _onWriteParsed = this._register(new Emitter<void>());
47
47
  public readonly onWriteParsed = this._onWriteParsed.event;
48
48
 
49
49
  constructor(private _action: (data: string | Uint8Array, promiseResult?: boolean) => void | Promise<boolean>) {
50
50
  super();
51
+ this._register(toDisposable(() => {
52
+ this._writeBuffer.length = 0;
53
+ this._callbacks.length = 0;
54
+ this._pendingData = 0;
55
+ this._bufferOffset = 0;
56
+ }));
51
57
  }
52
58
 
53
59
  public handleUserInput(): void {
54
60
  this._didUserInput = true;
55
61
  }
56
62
 
63
+ /**
64
+ * Flushes all pending writes synchronously. This is useful when you need to
65
+ * ensure all queued data is processed before performing an operation that
66
+ * depends upon everything being parsed like resize.
67
+ *
68
+ * Note: This is unreliable with async parser handlers as it does not wait for
69
+ * promises to resolve.
70
+ */
71
+ public flushSync(): void {
72
+ if (this._store.isDisposed) {
73
+ return;
74
+ }
75
+ // exit early if another sync write loop is active
76
+ if (this._isSyncWriting) {
77
+ return;
78
+ }
79
+ this._isSyncWriting = true;
80
+
81
+ // Process all pending chunks synchronously
82
+ let chunk: string | Uint8Array | undefined;
83
+ let didProcess = false;
84
+ while (chunk = this._writeBuffer.shift()) {
85
+ didProcess = true;
86
+ this._action(chunk);
87
+ const cb = this._callbacks.shift();
88
+ if (cb) cb();
89
+ }
90
+
91
+ // Reset buffer state
92
+ this._pendingData = 0;
93
+ this._bufferOffset = 0x7FFFFFFF;
94
+ this._writeBuffer.length = 0;
95
+ this._callbacks.length = 0;
96
+
97
+ this._isSyncWriting = false;
98
+ if (didProcess) {
99
+ this._onWriteParsed.fire();
100
+ }
101
+ }
102
+
57
103
  /**
58
104
  * @deprecated Unreliable, to be removed soon.
59
105
  */
60
106
  public writeSync(data: string | Uint8Array, maxSubsequentCalls?: number): void {
107
+ if (this._store.isDisposed) {
108
+ return;
109
+ }
61
110
  // stop writeSync recursions with maxSubsequentCalls argument
62
111
  // This is dangerous to use as it will lose the current data chunk
63
112
  // and return immediately.
@@ -101,7 +150,10 @@ export class WriteBuffer extends Disposable {
101
150
  }
102
151
 
103
152
  public write(data: string | Uint8Array, callback?: () => void): void {
104
- if (this._pendingData > DISCARD_WATERMARK) {
153
+ if (this._store.isDisposed) {
154
+ return;
155
+ }
156
+ if (this._pendingData > Constants.DISCARD_WATERMARK) {
105
157
  throw new Error('write data discarded, use flow control to avoid losing data');
106
158
  }
107
159
 
@@ -121,7 +173,7 @@ export class WriteBuffer extends Disposable {
121
173
  return;
122
174
  }
123
175
 
124
- setTimeout(() => this._innerWrite());
176
+ this._scheduleInnerWrite();
125
177
  }
126
178
 
127
179
  this._pendingData += data.length;
@@ -137,7 +189,7 @@ export class WriteBuffer extends Disposable {
137
189
  * effectively lowering the redrawing needs, schematically:
138
190
  *
139
191
  * macroTask _innerWrite:
140
- * if (performance.now() - (lastTime | 0) < WRITE_TIMEOUT_MS):
192
+ * if (performance.now() - (lastTime | 0) < Constants.WRITE_TIMEOUT_MS):
141
193
  * schedule microTask _innerWrite(lastTime)
142
194
  * else:
143
195
  * schedule macroTask _innerWrite(0)
@@ -157,7 +209,17 @@ export class WriteBuffer extends Disposable {
157
209
  *
158
210
  * Note, for pure sync code `lastTime` and `promiseResult` have no meaning.
159
211
  */
212
+ private _scheduleInnerWrite(lastTime: number = 0, promiseResult: boolean = true): void {
213
+ if (this._store.isDisposed) {
214
+ return;
215
+ }
216
+ this._innerWriteTimer.cancelAndSet(() => this._innerWrite(lastTime, promiseResult), 0);
217
+ }
218
+
160
219
  protected _innerWrite(lastTime: number = 0, promiseResult: boolean = true): void {
220
+ if (this._store.isDisposed) {
221
+ return;
222
+ }
161
223
  const startTime = lastTime || performance.now();
162
224
  while (this._writeBuffer.length > this._bufferOffset) {
163
225
  const data = this._writeBuffer[this._bufferOffset];
@@ -186,9 +248,16 @@ export class WriteBuffer extends Disposable {
186
248
  * responsibility to slice hard work), but we can at least schedule a screen update as we
187
249
  * gain control.
188
250
  */
189
- const continuation: (r: boolean) => void = (r: boolean) => performance.now() - startTime >= WRITE_TIMEOUT_MS
190
- ? setTimeout(() => this._innerWrite(0, r))
191
- : this._innerWrite(startTime, r);
251
+ const continuation: (r: boolean) => void = (r: boolean) => {
252
+ if (this._store.isDisposed) {
253
+ return;
254
+ }
255
+ if (performance.now() - startTime >= Constants.WRITE_TIMEOUT_MS) {
256
+ this._scheduleInnerWrite(0, r);
257
+ } else {
258
+ this._innerWrite(startTime, r);
259
+ }
260
+ };
192
261
 
193
262
  /**
194
263
  * Optimization considerations:
@@ -198,12 +267,12 @@ export class WriteBuffer extends Disposable {
198
267
  * this condition here, also the renderer has no way to spot nonsense updates either.
199
268
  * FIXME: A proper fix for this would track the FPS at the renderer entry level separately.
200
269
  *
201
- * If favoring of FPS shows bad throughtput impact, use the following instead. It favors
270
+ * If favoring of FPS shows bad throughput impact, use the following instead. It favors
202
271
  * throughput by eval'ing `startTime` upfront pulling at least one more chunk into the
203
272
  * current microtask queue (executed before setTimeout).
204
273
  */
205
274
  // const continuation: (r: boolean) => void = performance.now() - startTime >=
206
- // WRITE_TIMEOUT_MS
275
+ // Constants.WRITE_TIMEOUT_MS
207
276
  // ? r => setTimeout(() => this._innerWrite(0, r))
208
277
  // : r => this._innerWrite(startTime, r);
209
278
 
@@ -223,19 +292,19 @@ export class WriteBuffer extends Disposable {
223
292
  this._bufferOffset++;
224
293
  this._pendingData -= data.length;
225
294
 
226
- if (performance.now() - startTime >= WRITE_TIMEOUT_MS) {
295
+ if (performance.now() - startTime >= Constants.WRITE_TIMEOUT_MS) {
227
296
  break;
228
297
  }
229
298
  }
230
299
  if (this._writeBuffer.length > this._bufferOffset) {
231
300
  // Allow renderer to catch up before processing the next batch
232
301
  // trim already processed chunks if we are above threshold
233
- if (this._bufferOffset > WRITE_BUFFER_LENGTH_THRESHOLD) {
302
+ if (this._bufferOffset > Constants.WRITE_BUFFER_LENGTH_THRESHOLD) {
234
303
  this._writeBuffer = this._writeBuffer.slice(this._bufferOffset);
235
304
  this._callbacks = this._callbacks.slice(this._bufferOffset);
236
305
  this._bufferOffset = 0;
237
306
  }
238
- setTimeout(() => this._innerWrite());
307
+ this._scheduleInnerWrite();
239
308
  } else {
240
309
  this._writeBuffer.length = 0;
241
310
  this._callbacks.length = 0;
@@ -24,7 +24,7 @@ export function parseColor(data: string): [number, number, number] | undefined {
24
24
  if (!data) return;
25
25
  // also handle uppercases
26
26
  let low = data.toLowerCase();
27
- if (low.indexOf('rgb:') === 0) {
27
+ if (low.startsWith('rgb:')) {
28
28
  // 'rgb:' specifier
29
29
  low = low.slice(4);
30
30
  const m = RGB_REX.exec(low);
@@ -36,7 +36,7 @@ export function parseColor(data: string): [number, number, number] | undefined {
36
36
  Math.round(parseInt(m[3] || m[6] || m[9] || m[12], 16) / base * 255)
37
37
  ];
38
38
  }
39
- } else if (low.indexOf('#') === 0) {
39
+ } else if (low.startsWith('#')) {
40
40
  // '#' specifier
41
41
  low = low.slice(1);
42
42
  if (HASH_REX.exec(low) && [3, 6, 9, 12].includes(low.length)) {