mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-03-16 05:34:00 -04:00
main: make all polled FDs non-blocking
And handle read() returning EAGAIN. This fixes an issue with the keyboard repeat timer, which sometimes would return EAGAIN. Most likely because POLLIN was set on it, but then before we get to handle it, the timer was canceled (by a key up event).
This commit is contained in:
parent
c06f141189
commit
ae84f0ee00
1 changed files with 73 additions and 56 deletions
129
main.c
129
main.c
|
|
@ -250,10 +250,10 @@ main(int argc, char *const *argv)
|
||||||
.auto_margin = true,
|
.auto_margin = true,
|
||||||
.window_title_stack = tll_init(),
|
.window_title_stack = tll_init(),
|
||||||
.flash = {
|
.flash = {
|
||||||
.fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC),
|
.fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK),
|
||||||
},
|
},
|
||||||
.blink = {
|
.blink = {
|
||||||
.fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC),
|
.fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK),
|
||||||
},
|
},
|
||||||
.vt = {
|
.vt = {
|
||||||
.state = 1, /* STATE_GROUND */
|
.state = 1, /* STATE_GROUND */
|
||||||
|
|
@ -264,7 +264,7 @@ main(int argc, char *const *argv)
|
||||||
},
|
},
|
||||||
.kbd = {
|
.kbd = {
|
||||||
.repeat = {
|
.repeat = {
|
||||||
.fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC),
|
.fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
.colors = {
|
.colors = {
|
||||||
|
|
@ -614,8 +614,22 @@ main(int argc, char *const *argv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int timeout_ms = -1;
|
|
||||||
|
|
||||||
|
{
|
||||||
|
int fd = wl_display_get_fd(term.wl.display);
|
||||||
|
int fd_flags = fcntl(fd, F_GETFL);
|
||||||
|
if (fd_flags == -1) {
|
||||||
|
LOG_ERRNO("failed to set non blocking mode on Wayland display connection");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (fcntl(fd, F_SETFL, fd_flags | O_NONBLOCK) == -1) {
|
||||||
|
LOG_ERRNO("failed to set non blocking mode on Wayland display connection");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int timeout_ms = -1;
|
||||||
while (true) {
|
while (true) {
|
||||||
struct pollfd fds[] = {
|
struct pollfd fds[] = {
|
||||||
{.fd = wl_display_get_fd(term.wl.display), .events = POLLIN},
|
{.fd = wl_display_get_fd(term.wl.display), .events = POLLIN},
|
||||||
|
|
@ -660,41 +674,42 @@ main(int argc, char *const *argv)
|
||||||
if (fds[1].revents & POLLIN) {
|
if (fds[1].revents & POLLIN) {
|
||||||
uint8_t data[24 * 1024];
|
uint8_t data[24 * 1024];
|
||||||
ssize_t count = read(term.ptmx, data, sizeof(data));
|
ssize_t count = read(term.ptmx, data, sizeof(data));
|
||||||
if (count < 0) {
|
if (count < 0 && errno != EAGAIN) {
|
||||||
if (errno != EAGAIN)
|
LOG_ERRNO("failed to read from pseudo terminal");
|
||||||
LOG_ERRNO("failed to read from pseudo terminal");
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
vt_from_slave(&term, data, count);
|
if (count > 0) {
|
||||||
|
vt_from_slave(&term, data, count);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We likely need to re-render. But, we don't want to do
|
* We likely need to re-render. But, we don't want to
|
||||||
* it immediately. Often, a single client operation is
|
* do it immediately. Often, a single client operation
|
||||||
* done through multiple writes. Many times, we're so fast
|
* is done through multiple writes. Many times, we're
|
||||||
* that we render mid-operation frames.
|
* so fast that we render mid-operation frames.
|
||||||
*
|
*
|
||||||
* For example, we might end up rendering a frame where
|
* For example, we might end up rendering a frame
|
||||||
* the client just erased a line, while in the next frame,
|
* where the client just erased a line, while in the
|
||||||
* the client wrote to the same line. This causes screen
|
* next frame, the client wrote to the same line. This
|
||||||
* "flashes".
|
* causes screen "flashes".
|
||||||
*
|
*
|
||||||
* Mitigate by always incuring a small delay before
|
* Mitigate by always incuring a small delay before
|
||||||
* rendering the next frame. This gives the client some
|
* rendering the next frame. This gives the client
|
||||||
* time to finish the operation (and thus gives us time to
|
* some time to finish the operation (and thus gives
|
||||||
* receive the last writes before doing any actual
|
* us time to receive the last writes before doing any
|
||||||
* rendering).
|
* actual rendering).
|
||||||
*
|
*
|
||||||
* Note that when the client is producing data at a very
|
* Note that when the client is producing data at a
|
||||||
* high pace, we're rate limited by the wayland compositor
|
* very high pace, we're rate limited by the wayland
|
||||||
* anyway. The delay we introduce here only has any effect
|
* compositor anyway. The delay we introduce here only
|
||||||
* when the renderer is idle.
|
* has any effect when the renderer is idle.
|
||||||
*
|
*
|
||||||
* TODO: this adds input latency. Can we somehow hint
|
* TODO: this adds input latency. Can we somehow hint
|
||||||
* ourselves we just received keyboard input, and in this
|
* ourselves we just received keyboard input, and in
|
||||||
* case *not* delay rendering?
|
* this case *not* delay rendering?
|
||||||
*/
|
*/
|
||||||
timeout_ms = 1;
|
timeout_ms = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fds[1].revents & POLLHUP) {
|
if (fds[1].revents & POLLHUP) {
|
||||||
|
|
@ -707,9 +722,9 @@ main(int argc, char *const *argv)
|
||||||
ssize_t ret = read(
|
ssize_t ret = read(
|
||||||
term.kbd.repeat.fd, &expiration_count, sizeof(expiration_count));
|
term.kbd.repeat.fd, &expiration_count, sizeof(expiration_count));
|
||||||
|
|
||||||
if (ret < 0)
|
if (ret < 0 && errno != EAGAIN)
|
||||||
LOG_ERRNO("failed to read repeat key from repeat timer fd");
|
LOG_ERRNO("failed to read repeat key from repeat timer fd");
|
||||||
else {
|
else if (ret > 0) {
|
||||||
term.kbd.repeat.dont_re_repeat = true;
|
term.kbd.repeat.dont_re_repeat = true;
|
||||||
for (size_t i = 0; i < expiration_count; i++)
|
for (size_t i = 0; i < expiration_count; i++)
|
||||||
input_repeat(&term, term.kbd.repeat.key);
|
input_repeat(&term, term.kbd.repeat.key);
|
||||||
|
|
@ -722,15 +737,16 @@ main(int argc, char *const *argv)
|
||||||
ssize_t ret = read(
|
ssize_t ret = read(
|
||||||
term.flash.fd, &expiration_count, sizeof(expiration_count));
|
term.flash.fd, &expiration_count, sizeof(expiration_count));
|
||||||
|
|
||||||
if (ret < 0)
|
if (ret < 0 && errno != EAGAIN)
|
||||||
LOG_ERRNO("failed to read flash timer");
|
LOG_ERRNO("failed to read flash timer");
|
||||||
else
|
else if (ret > 0) {
|
||||||
LOG_DBG("flash timer expired %llu times",
|
LOG_DBG("flash timer expired %llu times",
|
||||||
(unsigned long long)expiration_count);
|
(unsigned long long)expiration_count);
|
||||||
|
|
||||||
term.flash.active = false;
|
term.flash.active = false;
|
||||||
term_damage_view(&term);
|
term_damage_view(&term);
|
||||||
render_refresh(&term);
|
render_refresh(&term);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fds[4].revents & POLLIN) {
|
if (fds[4].revents & POLLIN) {
|
||||||
|
|
@ -738,29 +754,30 @@ main(int argc, char *const *argv)
|
||||||
ssize_t ret = read(
|
ssize_t ret = read(
|
||||||
term.blink.fd, &expiration_count, sizeof(expiration_count));
|
term.blink.fd, &expiration_count, sizeof(expiration_count));
|
||||||
|
|
||||||
if (ret < 0)
|
if (ret < 0 && errno != EAGAIN)
|
||||||
LOG_ERRNO("failed to read blink timer");
|
LOG_ERRNO("failed to read blink timer");
|
||||||
else
|
else if (ret > 0) {
|
||||||
LOG_DBG("blink timer expired %llu times",
|
LOG_DBG("blink timer expired %llu times",
|
||||||
(unsigned long long)expiration_count);
|
(unsigned long long)expiration_count);
|
||||||
|
|
||||||
term.blink.state = term.blink.state == BLINK_ON
|
term.blink.state = term.blink.state == BLINK_ON
|
||||||
? BLINK_OFF : BLINK_ON;
|
? BLINK_OFF : BLINK_ON;
|
||||||
|
|
||||||
/* Scan all visible cells and mark rows with blinking cells dirty */
|
/* Scan all visible cells and mark rows with blinking cells dirty */
|
||||||
for (int r = 0; r < term.rows; r++) {
|
for (int r = 0; r < term.rows; r++) {
|
||||||
struct row *row = grid_row_in_view(term.grid, r);
|
struct row *row = grid_row_in_view(term.grid, r);
|
||||||
for (int col = 0; col < term.cols; col++) {
|
for (int col = 0; col < term.cols; col++) {
|
||||||
struct cell *cell = &row->cells[col];
|
struct cell *cell = &row->cells[col];
|
||||||
|
|
||||||
if (cell->attrs.blink) {
|
if (cell->attrs.blink) {
|
||||||
cell->attrs.clean = 0;
|
cell->attrs.clean = 0;
|
||||||
row->dirty = true;
|
row->dirty = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
render_refresh(&term);
|
render_refresh(&term);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue