-
Notifications
You must be signed in to change notification settings - Fork 332
fix: fall back to /dev/tty when stdin/stdout is not a tty #957
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
|
This might fix an issue I have in skim-rs/skim#653 |
|
Hey @LoricAndre, I'm not sure when this will be reviewed. If you want to give this branch a try, it would be helpful to see if it does fix your issue since the original problem that skim was facing (#396) is one of the main reasons I made this change. |
|
Changing from processing input from stdin to always using dev/tty seems like it would fundamentally change the behavior of apps. I'm not certain that this is desired in all cases. Can you talk a bit about the impact of the change and perhaps discuss where this might not be desired? A good way to think about this is put yourself in the perspective of an app developer who sees this change. What does it mean for their app? What does it mean if they hit a problem caused by this change? |
|
Input processing currently uses the The only scenario I can think of where explicitly reading from It wouldn't be too difficult to have another method or some conditional logic that prefers |
|
My question is not so much about whether the use case is rational, but more about whether by choosing this approach is a one way door. I.e. this change makes it impossible to use stdin instead of tty as the input. An example of where you might want to do this is nesting an inner TUI app inside an outer one and using the stdin / out as the means to control the inner tty from the outer. |
Fair point. I suppose it doesn't hurt to prefer using |
4e9fd94 to
74f0826
Compare
|
Actually, I don't think it matters here. If you're piping input from a parent process, |
|
Would it be better if I made a separate PR just for reading the cursor position from |
I think I'd like to see some narrative about the design of the changes in addition to the PR here. Capture the previous conversation / concerns, document how you expect this will work etc. Fall backs are often problematic from a design perspective as they're often invisible to the calling code, which makes them confusing for devs ("this worked the other day, what's different here") Having something to point at that describes the reason in a way that works for the dev and the app users seems like a good idea here. |
|
I just ran into this issue in mac, any idea when this will be merged? :) |
Maybe it would help to use some other well-known libraries for comparison. Here is a list of other examples and how they handle I/O: For input:
For output:
The changes here should be transparent and not break any existing code. Not that I can promise anything with 100% certainty; there are plenty of weird edge cases with terminals. I do think it would be beneficial to make an API that gives the user more control over the I/O sources, but that will require design discussion, probable breaking changes, etc. The fallback approach is already used in other popular libraries and I think it's probably the best way to make things "just work" without any breaking changes. The goal of this PR is to unblock people who want to make |
|
I will admit I don't have a super great reason for preferring the fallback method other than the fact that I've seen it done before by people that seem to know terminal mechanics well, so I opened a discussion on the bubbletea repo to see if I could hear more about their rationale for the design. |
|
I got a reply on my discussion that mostly confirms my suspicions - the fallback method is used to avoid opening an extra file descriptor when it's not necessary to do so. Crossterm already uses a fallback almost everywhere, just not when reading the cursor position, so that's the only real change here. If you want, we could just open a tty every time at the cost of opening extra file descriptors instead of reusing stdin/stdout. That's a pretty minor overhead. |
|
Can we please get some clear direction from the code maintainers? @TimonPost The function seems broken in its current state. If the library will never support the fallback method as fixed in this PR, the documentation should clearly state that the function will take 2 seconds to timeout if called with a piped stdout, i.e. it should at least mention this known bug/limitation. If backward compatibility is a concern, a feature flag could be introduced to allow the fallback behavior, provided it is properly documented. |
|
Taking a bit more of a look at each of the other terminal libraries (just the input side)
So I'd say that the common factor is that being explicit about things rather than falling back. Now that's not to say I don't think that using /dev/tty should be possible. I think it definitely should be possible to choose that the input comes from /dev/tty. But it should be an active application choice to do so rather than something that crossterm does without any notification to the app. Does that pushback make sense? It's worth thinking about situations where a terminal app might be hooked up to a non terminal file descriptor for whatever reason, where the size information can be provided by that means regardless of it not being a terminal.
This is actually a reasonable argument - what are the current places crossterm does fallbacks to /dev/tty that are comparable?
Technically, I'm a maintainer, but I'm responding with my user hat on rather than as a maintainer here. |
|
Hi @joshka, I understand the need for explicitness, but is the current position API (or any other cursor API) anywhere close to being explicit about how it works? If not, are you suggesting a completely new API or a set of APIs that would allow explicit control? As a user, I find this API unreliable, or rather unusable, as it’s unclear when it will work, leading to a 2-second delay whenever it fails. This seems inconsistent with your philosophy. Again, while a more explicit API design would be great, it just seems awkward in this case, as also the Windows implementation doesn’t seem to align with a more explicit approach. So, are you suggesting a Unix-specific variant that requires an explicit parameter or a more wide-spread re-design? |
Every other place that needs to read from the tty uses a fallback.
The change here unifies the logic for the latter two scenarios and makes the cursor position implementation behave the same way. I agree that it would be good to allow this to be more customizable, but @tugtugtug is correct in that it will require some additional refactoring to expose these options everywhere they need to be used, as well as require platform-specific considerations ( |
I just want to clarify my involvement here. I'm a maintainer of the Ratatui crate mainly;I have maintainer access on the crossterm repo mainly because I volunteered to help Timon get some long outstanding issues and PRs reviewed and merged. That doesn't make me the expert / gatekeeper on what goes on with these things. It's highly likely that there are many users that are significantly more knowledgeable about this library and the nuances than I am. So I want to make sure that anything that I'm saying in terms of my position on things is coming from the perspective of a user mostly. As a user of any API, I expect that when something fails I should get a reasonable failure message that helps me understand the failure. It's ok to create methods that do the right thing when that failure occurs, but care has to be taken to make sure that the original failure is available when that fallback is undesirable. Failling back to or explicitly opting in to using /dev/tty seems like a logical thing to want. But it also seems like there are use cases where this would not be ideal. Right now, we're in the situation where this doesn't have a fallback, so that's the existing expected behavior of any application that uses these methods. Changing that so that what was previously an error is now successfully doing something different is a breaking change for some use cases. Introducing a breaking change has a few options:
Each of these approaches has a certain amount of complexity. I don't like option 1 very much at all, so choosing to do anything else would be my preference. With that said, I'm not an expert on the choices made internally to the library already, so if a fallback is more consistent, then that makes a bunch of sense. The use cases I'm interested in are ptys, things like tuis that are hooked up to an ssh connection using russh, driving tuis to test them using piped input, ...
Thanks @aschey, I'll give the PR another solid read in light of that sometime this week. I think you might be right that this approach is the correct one given the existing behavior. (I.e. that this PR doesn't make things worse). As a related aside. There was a discovery in #996 that macOS can actually use kqueue and mio against /dev/ttys000 (where 000 is the terminal attached to the process), just not /dev/tty. So something around that might be worth considering for the fallback instead of /dev/tty. Aside 2: Given that there's been no complaints about the new rustix implementation of things, it could be worth considering dropping the libc code altogether to simplify the amount of code in this area. When this was implemented we kept both because the change was fundamentally changing some of the deep internals. This might be worth considering as a future change (noting here as you're sort of in that code area). |
|
Any news about this ? I understand that this can be complicated to review, but this is actually a real issue in some contexts, like zsh widgets as mentioned above. |
|
termina handles this correctly if anyone needs this functionality. There isn't a published ratatui backend for it, but you can mostly copy it from helix's implementation. |

Resolves #919
Resolves #396
Supersedes #743
Previous attempts to read from
/dev/ttyhad issues because of problems with polling/dev/ttyon MacOS. This has been solved for a while with theuse-dev-ttyfeature, so now it should be safe to fall back to using/dev/ttyifstdin/stdoutis not a terminal everywhere except for the mio event reader on MacOS.This PR standardizes the use of
/dev/ttyfor both input and output by splitting thetty_fdfunction intotty_fd_inandtty_fd_outso both can handle the appropriate fallback logic forstdinandstdoutrespectively. The only place that needs special care is the MacOS event reader whenuse-dev-ttyis not enabled.The mio event reader for MacOS has been modified to return an explicit error message when used with piped input to inform users that
use-dev-ttyis required for such use cases. The error message was previously getting swallowed, but will now be returned to the caller.Finally, the logic to read the cursor position was always writing to
stdout, causing an error whenstdoutwas not a tty. This has been changed to use the newtty_fd_outfunction.This was tested on Mac, Linux, and WSL. Windows should be unaffected.