Skip to content
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

Only read/poll specific event. Using raw mode & reading resize events. #967

Open
joe-conigliaro opened this issue Feb 2, 2025 · 15 comments

Comments

@joe-conigliaro
Copy link

joe-conigliaro commented Feb 2, 2025

I have a terminal application where Im using raw mode, it works great. However, want to catch resize events using crossterm, but it polls for every event (keyboard input included) which messes up my code that reads from stdin. it juggles back and forth between my read and crossterm event read.

I would like to be able to only capture specific events, for example the window resize event. This way it would not interfere in any way with my normal input mechanism. I did see some of the internal functions take a filter, but they are not exposed (perhaps its not even what I want).

The alternative I see is to use crossterm to read keyboard input as well as resize events, but this would complicate my code greatly as I would need to convert every key and combination to its byte sequence. Using raw more is a very simple and effective solution.

Looking forward to hearing some feedback. Hopefully I missed a simple solution to this which exists.
Thanks in advance.

@TimonPost
Copy link
Member

Why not just have this filter your self? Should be a simple match function on the event. We have had this request before but think we would like crossterm to be a bit more close to OS and leave any of the filtering up to the user.

@joe-conigliaro
Copy link
Author

joe-conigliaro commented Feb 2, 2025

Why not just have this filter your self? Should be a simple match function on the event. We have had this request before but think we would like crossterm to be a bit more close to OS and leave any of the filtering up to the user.

Hi @TimonPost, thanks for the reply.

Thats the issue though, event.read() is reading all events. I have a thread running which reads user input from stdin in raw mode 1 char at a time. When I have event.read() also going in another thread it steals the input from my thread. so one byte will get read from my reader, and the next key event will be read by event.read().. and it alternates. if that makes sense? Unless I'm doing something stupid.

@TimonPost
Copy link
Member

That sounds like undefined behavior. Read is not guaranteeing a duplicated event source, it polls events from the OS of which there is only one resource. If you want multiple threads to read input, you would want to spawn one reading thread, and distribute events to the other threads that need the specific events you mention. Even if we build a filter you would still have event stealing in your current setup.

@joe-conigliaro
Copy link
Author

That sounds like undefined behavior. Read is not guaranteeing a duplicated event source, it polls events from the OS of which there is only one resource. If you want multiple threads to read input, you would want to spawn one reading thread, and distribute events to the other threads that need the specific events you mention. Even if we build a filter you would still have event stealing in your current setup.

That makes sense, let me try and implement it that way. Thanks!

@joe-conigliaro
Copy link
Author

joe-conigliaro commented Feb 2, 2025

@TimonPost I'm still not sure how I can achieve what I want. Im happy to do reading from a single thread, however that would mean using my own read method or crossterm::event::read(). This still leaves me with the issue of getting raw input. Could you please show a simple example of how it could be done?

@joe-conigliaro
Copy link
Author

joe-conigliaro commented Feb 2, 2025

I think I worked something out using crossterm::event::poll()

edit: I cant seem to achieve what I want, I will ponder this some more.

@joe-conigliaro
Copy link
Author

joe-conigliaro commented Feb 3, 2025

@TimonPost I didn't really look how the internal event reader works, but my original threaded code was working on the assumption I could somehow only capture the resize event, and it would not interfere with my reading form stdin. I didn't realise the resize event & reading from stdin would use the same source.

The only option in this case will be to poll for an event and match only resize events, and I will also need to do my own polling when reading stdin from the same thread/loop. This should achieve what I want, I will test it when I get a chance.

@aschey
Copy link
Contributor

aschey commented Feb 3, 2025

I don't think you can split up the polling that way because if some input comes in from stdin while you're polling for a resize and vice versa, then it's going to get swallowed.

If you don't care about Windows support, then you can forgo using crossterm's event reader and listen for SIGWINCH to catch resizes using signal_hook or similar.

What's your requirement for needing the un-parsed input? If you just need the ANSI-encoded event representations, I have a crate terminput that can re-encode events into their ANSI sequences.

@joe-conigliaro
Copy link
Author

I don't think you can split up the polling that way because if some input comes in from stdin while you're polling for a resize and vice versa, then it's going to get swallowed.

If you don't care about Windows support, then you can forego using crossterm's event reader and listen for SIGWINCH to catch resizes using signal_hook or similar.

What's your requirement for needing the un-parsed input? If you just need the ANSI-encoded event representations, I have a crate terminput that can re-encode events into their ANSI sequences.

Yeah it didn't initially cross my mind that resize would also come from stdin.
I was hoping to avoid converting all the sequences manually, using raw input from stdin was simple and all that was needed for my code. Since your crate exists, I might just give that a go! I will re-assess this, It just seems very complex for such a simple thing i want to do.

Thanks a lot for your reply!

@aschey
Copy link
Contributor

aschey commented Feb 4, 2025

Sure thing. It is deceptively complicated when you need cross-platform compatibility. Just to be clear, resizes don't come from stdin. The reason for having resizes come from the same stream as inputs is because of how Windows works. Windows doesn't send them over stdin either, but it does use the same stream for both.

@joe-conigliaro
Copy link
Author

joe-conigliaro commented Feb 4, 2025

Sure thing. It is deceptively complicated when you need cross-platform compatibility. Just to be clear, resizes don't come from stdin. The reason for having resizes come from the same stream as inputs is because of how Windows works. Windows doesn't send them over stdin either, but it does use the same stream for both.

Indeed it is very deceptive. I would like to keep it cross platform if possible. Thanks again for the explanation, I was wondering what the reason for that was.

@joe-conigliaro
Copy link
Author

One thing I meant to mention when I created this issue... it would have been nice if the original unparsed input was stored with the event.

@aschey
Copy link
Contributor

aschey commented Feb 4, 2025

The problem with that is cross-platform compatibility again. The source inputs on Windows are input records which are very different from ANSI escape codes.

@joe-conigliaro
Copy link
Author

joe-conigliaro commented Feb 4, 2025

The problem with that is cross-platform compatibility again. The source inputs on Windows are input records which are very different from ANSI escape codes.

This is a great point, since my testing was only on macos/linux it was something which hadn't even crossed my mind yet. I am happy to report that I tested your crate, and it works great for this. And as you have just pointed out, now it will truly be cross platform. Looking at it form this perspective now this is a good solution.

Thanks for taking the time to comment here, it was very helpful. I had overlooked various simple but important aspects (which indeed complicate things).
I think we can close this issue now.

@aschey
Copy link
Contributor

aschey commented Feb 4, 2025

Thanks! Let me know if you run into any issues with it. The encoding logic is pretty complicated and hasn't been battle tested all that much yet, so I'd be surprised if there aren't a few things that I overlooked.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants