terminal: sync redraw events with vertical blanks
We used an FPS timer to control redraw events but this caused some unwanted artifacts. Instead, we should always wait for the page-flip event before redrawing the screen. This can still be optimized (for instance via triple-buffering) but it works very nice now. Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
This commit is contained in:
parent
5c527e7fec
commit
19c2991fbb
@ -56,6 +56,9 @@ struct screen {
|
||||
struct kmscon_font *font;
|
||||
struct kmscon_font *bold_font;
|
||||
struct kmscon_text *txt;
|
||||
|
||||
bool swapping;
|
||||
bool pending;
|
||||
};
|
||||
|
||||
struct kmscon_terminal {
|
||||
@ -74,78 +77,67 @@ struct kmscon_terminal {
|
||||
unsigned int min_rows;
|
||||
|
||||
unsigned long fps;
|
||||
unsigned int redraw;
|
||||
struct ev_timer *redraw_timer;
|
||||
struct tsm_screen *console;
|
||||
struct tsm_vte *vte;
|
||||
struct kmscon_pty *pty;
|
||||
struct ev_fd *ptyfd;
|
||||
};
|
||||
|
||||
static void redraw(struct kmscon_terminal *term)
|
||||
static void do_redraw_screen(struct screen *scr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!scr->term->awake)
|
||||
return;
|
||||
|
||||
scr->pending = false;
|
||||
tsm_screen_draw(scr->term->console, kmscon_text_prepare_cb,
|
||||
kmscon_text_draw_cb, kmscon_text_render_cb, scr->txt);
|
||||
ret = uterm_display_swap(scr->disp);
|
||||
if (ret) {
|
||||
log_warning("cannot swap display %p", scr->disp);
|
||||
return;
|
||||
}
|
||||
|
||||
scr->swapping = true;
|
||||
}
|
||||
|
||||
static void redraw_screen(struct screen *scr)
|
||||
{
|
||||
if (!scr->term->awake)
|
||||
return;
|
||||
|
||||
if (scr->swapping)
|
||||
scr->pending = true;
|
||||
else
|
||||
do_redraw_screen(scr);
|
||||
}
|
||||
|
||||
static void redraw_all(struct kmscon_terminal *term)
|
||||
{
|
||||
struct shl_dlist *iter;
|
||||
struct screen *ent;
|
||||
struct screen *scr;
|
||||
|
||||
if (!term->awake)
|
||||
return;
|
||||
|
||||
shl_dlist_for_each(iter, &term->screens) {
|
||||
ent = shl_dlist_entry(iter, struct screen, list);
|
||||
|
||||
tsm_screen_draw(term->console,
|
||||
kmscon_text_prepare_cb,
|
||||
kmscon_text_draw_cb,
|
||||
kmscon_text_render_cb,
|
||||
ent->txt);
|
||||
uterm_display_swap(ent->disp);
|
||||
scr = shl_dlist_entry(iter, struct screen, list);
|
||||
redraw_screen(scr);
|
||||
}
|
||||
}
|
||||
|
||||
static void redraw_timer_event(struct ev_timer *timer, uint64_t num, void *data)
|
||||
static void display_event(struct uterm_display *disp,
|
||||
struct uterm_display_event *ev, void *data)
|
||||
{
|
||||
struct kmscon_terminal *term = data;
|
||||
struct screen *scr = data;
|
||||
|
||||
/* When a redraw is scheduled, the redraw-counter is set to the current
|
||||
* frame-rate. If this counter is full, we know that data changed and we
|
||||
* need to redraw the terminal. We then decrement the counter until it
|
||||
* drops to zero. This guarantees that we stay active for 1s without
|
||||
* a call to ev_timer_enable/disable() which required syscalls which can
|
||||
* be quite slow.
|
||||
* If a redraw is schedule in the meantime, the counter is reset to the
|
||||
* framerate and we have to redraw the screen. If it drops to zero, that
|
||||
* is, 1s after the last redraw, we disable the timer to stop consuming
|
||||
* CPU-power.
|
||||
* TODO: On _really_ slow machines we might want to avoid fps-limits
|
||||
* and redraw on change. We also should check whether waking up for the
|
||||
* fps-timeouts for 1s is really faster than calling
|
||||
* ev_timer_enable/disable() all the time. */
|
||||
|
||||
if (num > 1)
|
||||
log_debug("CPU is too slow; skipping %" PRIu64 " frames", num - 1);
|
||||
|
||||
if (term->redraw-- != term->fps) {
|
||||
if (!term->redraw) {
|
||||
ev_timer_disable(term->redraw_timer);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
redraw(term);
|
||||
}
|
||||
|
||||
static void schedule_redraw(struct kmscon_terminal *term)
|
||||
{
|
||||
if (!term->awake)
|
||||
if (ev->action != UTERM_PAGE_FLIP)
|
||||
return;
|
||||
|
||||
if (!term->redraw) {
|
||||
ev_timer_enable(term->redraw_timer);
|
||||
ev_timer_drain(term->redraw_timer, NULL);
|
||||
}
|
||||
|
||||
if (term->redraw < term->fps)
|
||||
term->redraw = term->fps;
|
||||
scr->swapping = false;
|
||||
if (scr->pending)
|
||||
do_redraw_screen(scr);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -182,7 +174,7 @@ static void terminal_resize(struct kmscon_terminal *term,
|
||||
/* shrinking always succeeds */
|
||||
tsm_screen_resize(term->console, term->min_cols, term->min_rows);
|
||||
kmscon_pty_resize(term->pty, term->min_cols, term->min_rows);
|
||||
schedule_redraw(term);
|
||||
redraw_all(term);
|
||||
}
|
||||
|
||||
static int add_display(struct kmscon_terminal *term, struct uterm_display *disp)
|
||||
@ -214,10 +206,16 @@ static int add_display(struct kmscon_terminal *term, struct uterm_display *disp)
|
||||
scr->term = term;
|
||||
scr->disp = disp;
|
||||
|
||||
ret = uterm_display_register_cb(scr->disp, display_event, scr);
|
||||
if (ret) {
|
||||
log_error("cannot register display callback: %d", ret);
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
ret = kmscon_font_find(&scr->font, &attr, term->conf->font_engine);
|
||||
if (ret) {
|
||||
log_error("cannot create font");
|
||||
goto err_free;
|
||||
goto err_cb;
|
||||
}
|
||||
|
||||
attr.bold = true;
|
||||
@ -255,7 +253,7 @@ static int add_display(struct kmscon_terminal *term, struct uterm_display *disp)
|
||||
shl_dlist_link(&term->screens, &scr->list);
|
||||
|
||||
log_debug("added display %p to terminal %p", disp, term);
|
||||
schedule_redraw(term);
|
||||
redraw_screen(scr);
|
||||
uterm_display_ref(scr->disp);
|
||||
return 0;
|
||||
|
||||
@ -264,6 +262,8 @@ err_text:
|
||||
err_font:
|
||||
kmscon_font_unref(scr->bold_font);
|
||||
kmscon_font_unref(scr->font);
|
||||
err_cb:
|
||||
uterm_display_unregister_cb(scr->disp, display_event, scr);
|
||||
err_free:
|
||||
free(scr);
|
||||
return ret;
|
||||
@ -329,28 +329,28 @@ static void input_event(struct uterm_input *input,
|
||||
if (conf_grab_matches(term->conf->grab_scroll_up,
|
||||
ev->mods, ev->num_syms, ev->keysyms)) {
|
||||
tsm_screen_sb_up(term->console, 1);
|
||||
schedule_redraw(term);
|
||||
redraw_all(term);
|
||||
ev->handled = true;
|
||||
return;
|
||||
}
|
||||
if (conf_grab_matches(term->conf->grab_scroll_down,
|
||||
ev->mods, ev->num_syms, ev->keysyms)) {
|
||||
tsm_screen_sb_down(term->console, 1);
|
||||
schedule_redraw(term);
|
||||
redraw_all(term);
|
||||
ev->handled = true;
|
||||
return;
|
||||
}
|
||||
if (conf_grab_matches(term->conf->grab_page_up,
|
||||
ev->mods, ev->num_syms, ev->keysyms)) {
|
||||
tsm_screen_sb_page_up(term->console, 1);
|
||||
schedule_redraw(term);
|
||||
redraw_all(term);
|
||||
ev->handled = true;
|
||||
return;
|
||||
}
|
||||
if (conf_grab_matches(term->conf->grab_page_down,
|
||||
ev->mods, ev->num_syms, ev->keysyms)) {
|
||||
tsm_screen_sb_page_down(term->console, 1);
|
||||
schedule_redraw(term);
|
||||
redraw_all(term);
|
||||
ev->handled = true;
|
||||
return;
|
||||
}
|
||||
@ -364,7 +364,7 @@ static void input_event(struct uterm_input *input,
|
||||
if (tsm_vte_handle_keyboard(term->vte, ev->keysyms[0], ev->ascii,
|
||||
ev->mods, ev->codepoints[0])) {
|
||||
tsm_screen_sb_reset(term->console);
|
||||
schedule_redraw(term);
|
||||
redraw_all(term);
|
||||
ev->handled = true;
|
||||
}
|
||||
}
|
||||
@ -399,7 +399,7 @@ static int terminal_open(struct kmscon_terminal *term)
|
||||
}
|
||||
|
||||
term->opened = true;
|
||||
schedule_redraw(term);
|
||||
redraw_all(term);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -415,8 +415,6 @@ static void terminal_destroy(struct kmscon_terminal *term)
|
||||
|
||||
terminal_close(term);
|
||||
rm_all_screens(term);
|
||||
ev_eloop_rm_timer(term->redraw_timer);
|
||||
ev_timer_unref(term->redraw_timer);
|
||||
uterm_input_unregister_cb(term->input, input_event, term);
|
||||
ev_eloop_rm_fd(term->ptyfd);
|
||||
kmscon_pty_unref(term->pty);
|
||||
@ -443,7 +441,7 @@ static int session_event(struct kmscon_session *session,
|
||||
term->awake = true;
|
||||
if (!term->opened)
|
||||
terminal_open(term);
|
||||
schedule_redraw(term);
|
||||
redraw_all(term);
|
||||
break;
|
||||
case KMSCON_SESSION_DEACTIVATE:
|
||||
term->awake = false;
|
||||
@ -465,7 +463,7 @@ static void pty_input(struct kmscon_pty *pty, const char *u8, size_t len,
|
||||
terminal_open(term);
|
||||
} else {
|
||||
tsm_vte_input(term->vte, u8, len);
|
||||
schedule_redraw(term);
|
||||
redraw_all(term);
|
||||
}
|
||||
}
|
||||
|
||||
@ -565,21 +563,11 @@ int kmscon_terminal_register(struct kmscon_session **out,
|
||||
spec.it_value.tv_nsec = 1;
|
||||
spec.it_interval.tv_nsec = fps;
|
||||
|
||||
ret = ev_timer_new(&term->redraw_timer, &spec, redraw_timer_event,
|
||||
term, log_llog);
|
||||
if (ret)
|
||||
goto err_input;
|
||||
ev_timer_disable(term->redraw_timer);
|
||||
|
||||
ret = ev_eloop_add_timer(term->eloop, term->redraw_timer);
|
||||
if (ret)
|
||||
goto err_timer;
|
||||
|
||||
ret = kmscon_seat_register_session(seat, &term->session, session_event,
|
||||
term);
|
||||
if (ret) {
|
||||
log_error("cannot register session for terminal: %d", ret);
|
||||
goto err_redraw;
|
||||
goto err_input;
|
||||
}
|
||||
|
||||
ev_eloop_ref(term->eloop);
|
||||
@ -588,10 +576,6 @@ int kmscon_terminal_register(struct kmscon_session **out,
|
||||
log_debug("new terminal object %p", term);
|
||||
return 0;
|
||||
|
||||
err_redraw:
|
||||
ev_eloop_rm_timer(term->redraw_timer);
|
||||
err_timer:
|
||||
ev_timer_unref(term->redraw_timer);
|
||||
err_input:
|
||||
uterm_input_unregister_cb(term->input, input_event, term);
|
||||
err_ptyfd:
|
||||
|
Loading…
x
Reference in New Issue
Block a user