Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
### main/HEAD
* Update keypress reading logic to better handle copy/paste in the future

### v4.0.8 - 25/Dec/2025
* Add new Cursor::move overload to accept two integers
Expand Down
70 changes: 54 additions & 16 deletions rawterm/core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
#include "cursor.h"
#include "exceptions.h"

#if __linux__
#include <fcntl.h>
#endif

namespace rawterm {
namespace detail {

Expand Down Expand Up @@ -45,6 +49,10 @@ namespace rawterm {

void disable_raw_mode() {
#if __linux__
if (detail::stdin_orig_flags != -1) {
fcntl(STDIN_FILENO, F_SETFL, detail::stdin_orig_flags);
detail::stdin_orig_flags = -1;
}
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &rawterm::detail::orig) == -1) {
Cursor c;
c.reset();
Expand Down Expand Up @@ -73,6 +81,11 @@ namespace rawterm {
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1) {
std::perror("tcsetattr");
}
const int flags = fcntl(STDIN_FILENO, F_GETFL);
if (flags != -1) {
detail::stdin_orig_flags = flags;
fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
}
return 0;
#elif _WIN32
// https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#example-of-sgr-terminal-sequences
Expand Down Expand Up @@ -141,28 +154,53 @@ namespace rawterm {
}
}

[[nodiscard]] const std::optional<rawterm::Key> process_keypress() {
std::string characters = std::string(32, '\0');
int pollResult = poll(&detail::fd, 1, 0);
[[nodiscard]] std::string read_input() {
std::vector<char> buffer;
buffer.reserve(1024);
const int pollResult = poll(&detail::fd, 1, 0);

if (pollResult < 0) {
if (errno == EINTR) return {}; // Interrupted by signal
throw rawterm::KeypressError(
std::format("A poll error occured: {} - {}", errno, std::strerror(errno)));
}

// no input found
if (pollResult == 0) {
return {};
}
while (true) {
std::vector<char> chunk;
chunk.resize(1024);

// input available
if (pollResult > 0) {
if (read(STDIN_FILENO, characters.data(), 32) < 0) {
const int bytes_read = read(STDIN_FILENO, chunk.data(), chunk.size());

if (bytes_read == 0) {
break;
} // EOF
if (bytes_read < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
break;
}
if (errno == EINTR) continue; // Interrupted, try again
throw rawterm::KeypressError("An error occured during reading user input");
}

// no input found
} else if (pollResult == 0) {
return {};
if (bytes_read) {
buffer.insert(buffer.end(), chunk.begin(), chunk.begin() + bytes_read);
}
}

// interrupted system call -- SIGWINCH interrupting poll()
} else if (errno == 4) {
return {};
return std::string(buffer.begin(), buffer.end());
}

// Error
} else {
throw rawterm::KeypressError(
std::format("A poll error occured: {} - {}", errno, std::strerror(errno)));
// TODO: Possibly update to return a vector<Key> instead?
// This will be a breaking change as we'll be updating the core interface
// Users will have to iterate over the returned keys
[[nodiscard]] const std::optional<rawterm::Key> process_keypress() {
const std::string characters = read_input();
if (!characters.size()) {
return {};
}

std::stringstream ss;
Expand Down
3 changes: 2 additions & 1 deletion rawterm/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,12 @@ namespace rawterm {
namespace detail {
#if __linux__
inline termios orig;
inline int stdin_orig_flags = -1;
#endif
inline Signal sig_sent = Signal::NONE;

// Used for polling in process_keypress()
inline pollfd fd {STDIN_FILENO, POLLIN, POLLOUT};
inline pollfd fd = {STDIN_FILENO, POLLIN, 0};

// used for debugging
[[nodiscard]] bool is_debug();
Expand Down
Loading