Skip to content

Commit e0b129a

Browse files
committed
VTDemo: add a signal handler for SIGINT
Use `POSIXCore`'s `SignalHandler` to installer a signal handler for `SIGINT` to ensure that we restore the terminal state on `SIGINT`. Fixes: #8
1 parent 86720de commit e0b129a

2 files changed

Lines changed: 34 additions & 7 deletions

File tree

Package.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,6 @@ let _: Package =
2525
]),
2626
.executableTarget(name: "VTDemo", dependencies: [
2727
.target(name: "VirtualTerminal"),
28+
.product(name: "POSIXCore", package: "swift-platform-core", condition: .when(platforms: [.macOS, .linux])),
2829
]),
2930
])

Sources/VTDemo/VTDemo.swift

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import Foundation
55
import Geometry
66
import Primitives
77
import VirtualTerminal
8+
#if !os(Windows)
9+
import POSIXCore
10+
#endif
811

912
extension VTBuffer {
1013
internal var center: Point {
@@ -301,6 +304,23 @@ private func render(statistics: FrameStatistics, into buffer: inout VTBuffer) {
301304

302305
// MARK: - Application
303306

307+
func reset(terminal: some VTTerminal) async {
308+
await terminal <<< .ResetMode([.DEC(.UseAlternateScreenBufferSaveCursor)])
309+
<<< .SetMode([.DEC(.TextCursorEnableMode)])
310+
}
311+
312+
private func reset(terminal: some VTTerminal) {
313+
#if swift(>=6.2)
314+
if #available(macOS 26, *) {
315+
Task<Void, Never>.immediate { await reset(terminal: terminal) }
316+
} else {
317+
Task.synchronously { await reset(terminal: terminal) }
318+
}
319+
#else
320+
Task.synchronously { await reset(terminal: terminal) }
321+
#endif
322+
}
323+
304324
@main
305325
private struct VTDemo {
306326
internal enum ApplicationState {
@@ -384,16 +404,18 @@ private struct VTDemo {
384404
await terminal <<< .SetMode([.DEC(.UseAlternateScreenBufferSaveCursor)])
385405
<<< .ResetMode([.DEC(.TextCursorEnableMode)])
386406

387-
defer {
388-
Task.synchronously {
389-
await terminal <<< .SetMode([.DEC(.TextCursorEnableMode)])
390-
<<< .ResetMode([.DEC(.UseAlternateScreenBufferSaveCursor)])
391-
}
392-
}
407+
defer { reset(terminal: terminal) }
393408

394409
try await withThrowingTaskGroup(of: Void.self) { group in
395410
defer { group.cancelAll() }
396411

412+
#if !os(Windows)
413+
let signals = try await SignalHandler.install(SIGINT) { _ in
414+
reset(terminal: terminal)
415+
_exit(0)
416+
}
417+
#endif
418+
397419
// Input handling task
398420
group.addTask {
399421
for try await event in terminal.input {
@@ -405,7 +427,7 @@ private struct VTDemo {
405427
nonisolated(unsafe) var previous: ContinuousClock.Instant?
406428
nonisolated(unsafe) var profiler = VTProfiler(target: VTDemo.PreferredFPS)
407429
let link = VTDisplayLink(fps: VTDemo.PreferredFPS) { link in
408-
let ΔTime = previous.map { link.timestamp - $0 } ?? .zero
430+
let ΔTime = previous.map { link.timestamp - $0 } ?? .zero
409431

410432
previous = .now
411433
profiler.measure {
@@ -434,6 +456,10 @@ private struct VTDemo {
434456
link.add(to: &group)
435457

436458
try await group.next()
459+
460+
#if !os(Windows)
461+
signals.remove()
462+
#endif
437463
}
438464
}
439465
}

0 commit comments

Comments
 (0)