mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-03-22 05:33:45 -04:00
term: implement cursor blinking
Blinking can be enabled either by setting the cursor style with CSI Ps SP q and selecting a blinking style. Or, with 'CSI ? 12 h' Note that both affect the same internal state. I.e. you can disable blinking with CSI ? 12l after having selected a blinking cursor style. This is consistent with XTerm behavior.
This commit is contained in:
parent
5106937c7b
commit
7d29435d86
4 changed files with 93 additions and 9 deletions
11
csi.c
11
csi.c
|
|
@ -866,7 +866,7 @@ csi_dispatch(struct terminal *term, uint8_t final)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 12:
|
case 12:
|
||||||
/* Ignored */
|
term_cursor_blink_enable(term);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 25:
|
case 25:
|
||||||
|
|
@ -992,7 +992,7 @@ csi_dispatch(struct terminal *term, uint8_t final)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 12:
|
case 12:
|
||||||
/* Ignored */
|
term_cursor_blink_disable(term);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 25:
|
case 25:
|
||||||
|
|
@ -1224,9 +1224,10 @@ csi_dispatch(struct terminal *term, uint8_t final)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
term->cursor_blinking = param == 0 || param & 1;
|
if (param == 0 || param & 1)
|
||||||
if (term->cursor_blinking)
|
term_cursor_blink_enable(term);
|
||||||
LOG_WARN("unimplemented: blinking cursor");
|
else
|
||||||
|
term_cursor_blink_disable(term);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
18
render.c
18
render.c
|
|
@ -184,9 +184,13 @@ render_cell(struct terminal *term, pixman_image_t *pix,
|
||||||
int x = term->x_margin + col * width;
|
int x = term->x_margin + col * width;
|
||||||
int y = term->y_margin + row * height;
|
int y = term->y_margin + row * height;
|
||||||
|
|
||||||
bool block_cursor = has_cursor && term->cursor_style == CURSOR_BLOCK;
|
|
||||||
bool is_selected = coord_is_selected(term, col, row);
|
bool is_selected = coord_is_selected(term, col, row);
|
||||||
|
|
||||||
|
bool block_cursor =
|
||||||
|
has_cursor &&
|
||||||
|
term->cursor_style == CURSOR_BLOCK &&
|
||||||
|
term->cursor_blink.state == CURSOR_BLINK_ON;
|
||||||
|
|
||||||
uint32_t _fg = 0;
|
uint32_t _fg = 0;
|
||||||
uint32_t _bg = 0;
|
uint32_t _bg = 0;
|
||||||
|
|
||||||
|
|
@ -240,7 +244,7 @@ render_cell(struct terminal *term, pixman_image_t *pix,
|
||||||
&(pixman_rectangle16_t){x, y, cell_cols * width, height});
|
&(pixman_rectangle16_t){x, y, cell_cols * width, height});
|
||||||
|
|
||||||
/* Non-block cursors */
|
/* Non-block cursors */
|
||||||
if (has_cursor && !block_cursor) {
|
if (has_cursor && !block_cursor && term->cursor_blink.state == CURSOR_BLINK_ON) {
|
||||||
pixman_color_t cursor_color;
|
pixman_color_t cursor_color;
|
||||||
if (term->cursor_color.text >> 31) {
|
if (term->cursor_color.text >> 31) {
|
||||||
cursor_color = color_hex_to_pixman(term->cursor_color.cursor);
|
cursor_color = color_hex_to_pixman(term->cursor_color.cursor);
|
||||||
|
|
@ -524,6 +528,15 @@ grid_render(struct terminal *term)
|
||||||
/* Detect cursor movement - we don't dirty cells touched
|
/* Detect cursor movement - we don't dirty cells touched
|
||||||
* by the cursor, since only the final cell matters. */
|
* by the cursor, since only the final cell matters. */
|
||||||
all_clean = false;
|
all_clean = false;
|
||||||
|
|
||||||
|
/* Force cursor blink to ON, to avoid blinking while moving cursor */
|
||||||
|
term->render.last_cursor.blink_state = CURSOR_BLINK_ON;
|
||||||
|
term->cursor_blink.state = CURSOR_BLINK_ON;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (term->render.last_cursor.blink_state != term->cursor_blink.state) {
|
||||||
|
/* Need to re-draw cursor */
|
||||||
|
all_clean = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -675,6 +688,7 @@ grid_render(struct terminal *term)
|
||||||
|
|
||||||
cell->attrs.clean = 0;
|
cell->attrs.clean = 0;
|
||||||
term->render.last_cursor.cell = cell;
|
term->render.last_cursor.cell = cell;
|
||||||
|
term->render.last_cursor.blink_state = term->cursor_blink.state;
|
||||||
int cols_updated = render_cell(
|
int cols_updated = render_cell(
|
||||||
term, pix, cell, term->cursor.point.col, view_aligned_row, true);
|
term, pix, cell, term->cursor.point.col, view_aligned_row, true);
|
||||||
|
|
||||||
|
|
|
||||||
65
terminal.c
65
terminal.c
|
|
@ -275,6 +275,36 @@ fdm_blink(struct fdm *fdm, int fd, int events, void *data)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
fdm_cursor_blink(struct fdm *fdm, int fd, int events, void *data)
|
||||||
|
{
|
||||||
|
if (events & EPOLLHUP)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
struct terminal *term = data;
|
||||||
|
uint64_t expiration_count;
|
||||||
|
ssize_t ret = read(
|
||||||
|
term->cursor_blink.fd, &expiration_count, sizeof(expiration_count));
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
if (errno == EAGAIN)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
LOG_ERRNO("failed to read cursor blink timer");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("cursor blink timer expired %llu times",
|
||||||
|
(unsigned long long)expiration_count);
|
||||||
|
|
||||||
|
/* Invert blink state */
|
||||||
|
term->cursor_blink.state = term->cursor_blink.state == CURSOR_BLINK_ON
|
||||||
|
? CURSOR_BLINK_OFF : CURSOR_BLINK_ON;
|
||||||
|
|
||||||
|
render_refresh(term);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
fdm_delayed_render(struct fdm *fdm, int fd, int events, void *data)
|
fdm_delayed_render(struct fdm *fdm, int fd, int events, void *data)
|
||||||
{
|
{
|
||||||
|
|
@ -403,6 +433,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl,
|
||||||
int ptmx = -1;
|
int ptmx = -1;
|
||||||
int flash_fd = -1;
|
int flash_fd = -1;
|
||||||
int blink_fd = -1;
|
int blink_fd = -1;
|
||||||
|
int cursor_blink_fd = -1;
|
||||||
int delay_lower_fd = -1;
|
int delay_lower_fd = -1;
|
||||||
int delay_upper_fd = -1;
|
int delay_upper_fd = -1;
|
||||||
|
|
||||||
|
|
@ -420,6 +451,10 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl,
|
||||||
LOG_ERRNO("failed to create blink timer FD");
|
LOG_ERRNO("failed to create blink timer FD");
|
||||||
goto close_fds;
|
goto close_fds;
|
||||||
}
|
}
|
||||||
|
if ((cursor_blink_fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK)) == -1) {
|
||||||
|
LOG_ERRNO("failed to create cursor blink timer FD");
|
||||||
|
goto close_fds;
|
||||||
|
}
|
||||||
if ((delay_lower_fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK)) == -1 ||
|
if ((delay_lower_fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK)) == -1 ||
|
||||||
(delay_upper_fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK)) == -1)
|
(delay_upper_fd = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK)) == -1)
|
||||||
{
|
{
|
||||||
|
|
@ -438,6 +473,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl,
|
||||||
if (!fdm_add(fdm, ptmx, EPOLLIN, &fdm_ptmx, term) ||
|
if (!fdm_add(fdm, ptmx, EPOLLIN, &fdm_ptmx, term) ||
|
||||||
!fdm_add(fdm, flash_fd, EPOLLIN, &fdm_flash, term) ||
|
!fdm_add(fdm, flash_fd, EPOLLIN, &fdm_flash, term) ||
|
||||||
!fdm_add(fdm, blink_fd, EPOLLIN, &fdm_blink, term) ||
|
!fdm_add(fdm, blink_fd, EPOLLIN, &fdm_blink, term) ||
|
||||||
|
!fdm_add(fdm, cursor_blink_fd, EPOLLIN, &fdm_cursor_blink, term) ||
|
||||||
!fdm_add(fdm, delay_lower_fd, EPOLLIN, &fdm_delayed_render, term) ||
|
!fdm_add(fdm, delay_lower_fd, EPOLLIN, &fdm_delayed_render, term) ||
|
||||||
!fdm_add(fdm, delay_upper_fd, EPOLLIN, &fdm_delayed_render, term))
|
!fdm_add(fdm, delay_upper_fd, EPOLLIN, &fdm_delayed_render, term))
|
||||||
{
|
{
|
||||||
|
|
@ -489,6 +525,10 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl,
|
||||||
.origin = ORIGIN_ABSOLUTE,
|
.origin = ORIGIN_ABSOLUTE,
|
||||||
.default_cursor_style = conf->cursor.style,
|
.default_cursor_style = conf->cursor.style,
|
||||||
.cursor_style = conf->cursor.style,
|
.cursor_style = conf->cursor.style,
|
||||||
|
.cursor_blink = {
|
||||||
|
.state = CURSOR_BLINK_ON,
|
||||||
|
.fd = cursor_blink_fd,
|
||||||
|
},
|
||||||
.default_cursor_color = {
|
.default_cursor_color = {
|
||||||
.text = conf->cursor.color.text,
|
.text = conf->cursor.color.text,
|
||||||
.cursor = conf->cursor.color.cursor,
|
.cursor = conf->cursor.color.cursor,
|
||||||
|
|
@ -572,6 +612,7 @@ close_fds:
|
||||||
fdm_del(fdm, ptmx);
|
fdm_del(fdm, ptmx);
|
||||||
fdm_del(fdm, flash_fd);
|
fdm_del(fdm, flash_fd);
|
||||||
fdm_del(fdm, blink_fd);
|
fdm_del(fdm, blink_fd);
|
||||||
|
fdm_del(fdm, cursor_blink_fd);
|
||||||
fdm_del(fdm, delay_lower_fd);
|
fdm_del(fdm, delay_lower_fd);
|
||||||
fdm_del(fdm, delay_upper_fd);
|
fdm_del(fdm, delay_upper_fd);
|
||||||
|
|
||||||
|
|
@ -688,6 +729,7 @@ term_destroy(struct terminal *term)
|
||||||
|
|
||||||
fdm_del(term->fdm, term->delayed_render_timer.lower_fd);
|
fdm_del(term->fdm, term->delayed_render_timer.lower_fd);
|
||||||
fdm_del(term->fdm, term->delayed_render_timer.upper_fd);
|
fdm_del(term->fdm, term->delayed_render_timer.upper_fd);
|
||||||
|
fdm_del(term->fdm, term->cursor_blink.fd);
|
||||||
fdm_del(term->fdm, term->blink.fd);
|
fdm_del(term->fdm, term->blink.fd);
|
||||||
fdm_del(term->fdm, term->flash.fd);
|
fdm_del(term->fdm, term->flash.fd);
|
||||||
fdm_del(term->fdm, term->ptmx);
|
fdm_del(term->fdm, term->ptmx);
|
||||||
|
|
@ -878,7 +920,7 @@ term_reset(struct terminal *term, bool hard)
|
||||||
term->saved_cursor = (struct cursor){.point = {0, 0}};
|
term->saved_cursor = (struct cursor){.point = {0, 0}};
|
||||||
term->alt_saved_cursor = (struct cursor){.point = {0, 0}};
|
term->alt_saved_cursor = (struct cursor){.point = {0, 0}};
|
||||||
term->cursor_style = term->default_cursor_style;
|
term->cursor_style = term->default_cursor_style;
|
||||||
term->cursor_blinking = false;
|
term_cursor_blink_disable(term);
|
||||||
term->cursor_color.text = term->default_cursor_color.text;
|
term->cursor_color.text = term->default_cursor_color.text;
|
||||||
term->cursor_color.cursor = term->default_cursor_color.cursor;
|
term->cursor_color.cursor = term->default_cursor_color.cursor;
|
||||||
selection_cancel(term);
|
selection_cancel(term);
|
||||||
|
|
@ -1085,6 +1127,27 @@ term_cursor_down(struct terminal *term, int count)
|
||||||
term_cursor_to(term, term->cursor.point.row + move_amount, term->cursor.point.col);
|
term_cursor_to(term, term->cursor.point.row + move_amount, term->cursor.point.col);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
term_cursor_blink_enable(struct terminal *term)
|
||||||
|
{
|
||||||
|
static const struct itimerspec timer = {
|
||||||
|
.it_value = {.tv_sec = 0, .tv_nsec = 500000000},
|
||||||
|
.it_interval = {.tv_sec = 0, .tv_nsec = 500000000},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (timerfd_settime(term->cursor_blink.fd, 0, &timer, NULL) < 0)
|
||||||
|
LOG_ERRNO("failed to arm cursor blink timer");
|
||||||
|
|
||||||
|
term->cursor_blink.state = CURSOR_BLINK_ON;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
term_cursor_blink_disable(struct terminal *term)
|
||||||
|
{
|
||||||
|
term->cursor_blink.state = CURSOR_BLINK_ON;
|
||||||
|
timerfd_settime(term->cursor_blink.fd, 0, &(struct itimerspec){{0}}, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
term_scroll_partial(struct terminal *term, struct scroll_region region, int rows)
|
term_scroll_partial(struct terminal *term, struct scroll_region region, int rows)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -232,7 +232,10 @@ struct terminal {
|
||||||
struct cursor alt_saved_cursor;
|
struct cursor alt_saved_cursor;
|
||||||
enum cursor_style default_cursor_style;
|
enum cursor_style default_cursor_style;
|
||||||
enum cursor_style cursor_style;
|
enum cursor_style cursor_style;
|
||||||
bool cursor_blinking;
|
struct {
|
||||||
|
enum { CURSOR_BLINK_ON, CURSOR_BLINK_OFF } state;
|
||||||
|
int fd;
|
||||||
|
} cursor_blink;
|
||||||
struct {
|
struct {
|
||||||
uint32_t text;
|
uint32_t text;
|
||||||
uint32_t cursor;
|
uint32_t cursor;
|
||||||
|
|
@ -292,6 +295,7 @@ struct terminal {
|
||||||
struct coord actual; /* Absolute */
|
struct coord actual; /* Absolute */
|
||||||
struct coord in_view; /* Offset by view */
|
struct coord in_view; /* Offset by view */
|
||||||
struct cell *cell; /* For easy access to content */
|
struct cell *cell; /* For easy access to content */
|
||||||
|
int blink_state;
|
||||||
} last_cursor;
|
} last_cursor;
|
||||||
|
|
||||||
struct buffer *last_buf; /* Buffer we rendered to last time */
|
struct buffer *last_buf; /* Buffer we rendered to last time */
|
||||||
|
|
@ -345,6 +349,8 @@ void term_cursor_left(struct terminal *term, int count);
|
||||||
void term_cursor_right(struct terminal *term, int count);
|
void term_cursor_right(struct terminal *term, int count);
|
||||||
void term_cursor_up(struct terminal *term, int count);
|
void term_cursor_up(struct terminal *term, int count);
|
||||||
void term_cursor_down(struct terminal *term, int count);
|
void term_cursor_down(struct terminal *term, int count);
|
||||||
|
void term_cursor_blink_enable(struct terminal *term);
|
||||||
|
void term_cursor_blink_disable(struct terminal *term);
|
||||||
|
|
||||||
void term_scroll(struct terminal *term, int rows);
|
void term_scroll(struct terminal *term, int rows);
|
||||||
void term_scroll_reverse(struct terminal *term, int rows);
|
void term_scroll_reverse(struct terminal *term, int rows);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue