Fix auto-scroll and SIGINT/Ctrl+C on macOS#430
Open
botsdown wants to merge 3 commits intomigueldeicaza:mainfrom
Open
Fix auto-scroll and SIGINT/Ctrl+C on macOS#430botsdown wants to merge 3 commits intomigueldeicaza:mainfrom
botsdown wants to merge 3 commits intomigueldeicaza:mainfrom
Conversation
When drag-selecting text past the terminal edges, the view should
auto-scroll. This fix addresses multiple issues:
1. Added timer management:
- autoScrollTimer property to hold the timer reference
- startAutoScrollTimer() to create timer with .common run loop mode
- stopAutoScrollTimer() to invalidate and clean up
- Timer start/stop calls in mouseDragged() and cleanup in mouseUp()
2. Fixed scroll direction in scrollingTimerElapsed():
- Was calling scrollUp() in both branches
- Now correctly calls scrollDown() when autoScrollDelta > 0
3. Added selection extension during scroll:
- Calls selection.dragExtend() after scrolling to include newly
visible rows
4. Fixed scroll-down detection in mouseDragged():
- calculateMouseHit() clamps row to valid range, so scroll-down
condition was never triggered
- Now uses raw pixel coordinates from mouse event to detect when
mouse is outside view bounds
Uses .common run loop mode to ensure timer fires during mouse tracking.
The Subprocess path uses POSIX_SPAWN_SETSID which creates a new session without a controlling terminal. This breaks Ctrl+C (SIGINT) because: 1. The kernel's line discipline only generates SIGINT from ETX (^C) when the PTY slave is the controlling terminal 2. Without a controlling terminal, tcgetpgrp() returns -1 (ENOTTY) 3. shellPid is never set in the Subprocess path The forkpty path works correctly because it calls login_tty() which: - Calls setsid() to create a new session - Calls ioctl(TIOCSCTTY) to set the PTY as controlling terminal - Properly sets up stdin/stdout/stderr This change forces the forkpty path on macOS while preserving the Subprocess path for other platforms where it may be needed. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Owner
|
How does Control-C work, I am using it just fine, can you show me what seems to be the problem? |
Owner
|
The auto-scroll seems sensible, I think I can add that. |
Owner
Author
|
Interesting. Let me revisit that one and make sure I had the latest version
downloaded before testing again. Thanks!
…On Sat, Jan 10, 2026 at 12:40 PM Miguel de Icaza ***@***.***> wrote:
*migueldeicaza* left a comment (migueldeicaza/SwiftTerm#430)
<#430 (comment)>
Btw, I tried the reproduction steps you have, and for me, I get exactly
that without your change:
image.png (view on web)
<https://github.com/user-attachments/assets/c40721fe-b4b3-4c27-87b8-cbe221b8c3e5>
—
Reply to this email directly, view it on GitHub
<#430 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/BVJGVEPCRQCIRM4S3J57Y2D4GFBQTAVCNFSM6AAAAACRHS7XVGVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZTOMZTGM2TKMZUGM>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
Author
|
After more research, I think the difference might be which code path
SwiftTerm takes at compile time.
The current logic is:
#if canImport(Subprocess)
startProcessWithSubprocess(...)
#else
startProcessWithForkpty(...)
#endif
swift-subprocess was added to the Swift toolchain relatively recently. If
you're on an older Xcode/Swift version where canImport(Subprocess)
evaluates to false, you'd get the forkpty path automatically. Ctrl+C works
fine there because forkpty establishes a proper controlling terminal.
In my particular environment, Xcode 16.2 / Swift 6, canImport(Subprocess)
is true, so we take the Subprocess path.
The issue I am seeing is that the Subprocess implementation:
1. Creates a PTY pair with openpty()
2. Sets POSIX_SPAWN_SETSID to create a new session
3. But never calls ioctl(TIOCSCTTY) to establish the PTY as the controlling
terminal
Without a controlling terminal, the kernel's line discipline doesn't know
where to send SIGINT when it receives ETX (0x03). The character gets
written to the PTY, but no signal is generated.
Could you check which path you're hitting? Adding this would confirm:
#if canImport(Subprocess)
print("Using Subprocess path")
#else
print("Using forkpty path")
#endif
If you are on the Subprocess path and Ctrl+C still works for you, There may
be something else I'm missing or something unique about my environment.
…On Sat, Jan 10, 2026 at 1:30 PM Austin Lowry ***@***.***> wrote:
Interesting. Let me revisit that one and make sure I had the
latest version downloaded before testing again. Thanks!
On Sat, Jan 10, 2026 at 12:40 PM Miguel de Icaza ***@***.***>
wrote:
> *migueldeicaza* left a comment (migueldeicaza/SwiftTerm#430)
> <#430 (comment)>
>
> Btw, I tried the reproduction steps you have, and for me, I get exactly
> that without your change:
> image.png (view on web)
> <https://github.com/user-attachments/assets/c40721fe-b4b3-4c27-87b8-cbe221b8c3e5>
>
> —
> Reply to this email directly, view it on GitHub
> <#430 (comment)>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/BVJGVEPCRQCIRM4S3J57Y2D4GFBQTAVCNFSM6AAAAACRHS7XVGVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZTOMZTGM2TKMZUGM>
> .
> You are receiving this because you authored the thread.Message ID:
> ***@***.***>
>
|
Owner
|
I am on the aubprocess path |
Author
|
Well, not sure what the difference is then. Let me dig a little deeper
tomorrow. It must be something on my end. Thanks, Miguel!
…On Sun, Jan 11, 2026 at 2:13 PM Miguel de Icaza ***@***.***> wrote:
*migueldeicaza* left a comment (migueldeicaza/SwiftTerm#430)
<#430 (comment)>
I am on the aubprocess path
—
Reply to this email directly, view it on GitHub
<#430 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/BVJGVEK3HCZYDUFIOE7ZFDL4GKVE5AVCNFSM6AAAAACRHS7XVGVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZTOMZVGY3DINZWHA>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
Owner
|
I bet I know what it is. Did you remove the Sandbox entitlement? I bet the sandbox is failing. |
Author
|
I don't think so... but I will check on that today!
…On Wed, Jan 14, 2026 at 4:58 PM Miguel de Icaza ***@***.***> wrote:
*migueldeicaza* left a comment (migueldeicaza/SwiftTerm#430)
<#430 (comment)>
I bet I know what it is.
Did you remove the Sandbox entitlement? I bet the sandbox is failing.
—
Reply to this email directly, view it on GitHub
<#430 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/BVJGVEMMI6KVNJYX5DLAQ2D4G3C2XAVCNFSM6AAAAACRHS7XVGVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZTONJSGA4DQMRYGQ>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
Owner
|
Any updates? |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

This PR contains two fixes for macOS:
1. Fix auto-scroll timer for drag selection
When drag-selecting text past the terminal edges, the view should auto-scroll. This fix addresses multiple issues:
Added timer management:
Fixed scroll direction in scrollingTimerElapsed():
Added selection extension during scroll:
Fixed scroll-down detection in mouseDragged():
Uses .common run loop mode to ensure timer fires during mouse tracking.
2. Fix SIGINT/Ctrl+C by forcing forkpty on macOS
Problem
On modern macOS with swift-subprocess available,
LocalProcess.startProcess()uses thestartProcessWithSubprocess()path. This breaks Ctrl+C (SIGINT) because:POSIX_SPAWN_SETSIDwhich creates a new session without a controlling terminaltcgetpgrp(childfd)returns -1 (ENOTTY) because there's no controlling terminalshellPidis never set in the Subprocess path (remains 0)The result: pressing Ctrl+C shows
^Cbut doesn't interrupt the running process.Solution
Force the
forkptypath on macOS. Theforkptyfunction internally callslogin_tty()which:setsid()to create a new sessionioctl(TIOCSCTTY)to set the PTY slave as the controlling terminalThis is a minimal change - just a
#if os(macOS)conditional to preferforkptyon macOS while preserving the Subprocess path for other platforms.Testing
sleep 100^Cappears but sleep continuesecho $?shows 130 (128 + signal 2)References