diff --git a/fdm.c b/fdm.c index 4592a9e8..9d14dca8 100644 --- a/fdm.c +++ b/fdm.c @@ -21,11 +21,17 @@ struct handler { bool deleted; }; +struct hook { + fdm_hook_t callback; + void *callback_data; +}; + struct fdm { int epoll_fd; bool is_polling; tll(struct handler *) fds; tll(struct handler *) deferred_delete; + tll(struct hook) hooks; }; struct fdm * @@ -43,6 +49,7 @@ fdm_init(void) .is_polling = false, .fds = tll_init(), .deferred_delete = tll_init(), + .hooks = tll_init(), }; return fdm; } @@ -56,11 +63,16 @@ fdm_destroy(struct fdm *fdm) if (tll_length(fdm->fds) > 0) LOG_WARN("FD list not empty"); + if (tll_length(fdm->hooks) > 0) + LOG_WARN("hook list not empty"); + assert(tll_length(fdm->fds) == 0); assert(tll_length(fdm->deferred_delete) == 0); + assert(tll_length(fdm->hooks) == 0); tll_free(fdm->fds); tll_free(fdm->deferred_delete); + tll_free(fdm->hooks); close(fdm->epoll_fd); free(fdm); } @@ -195,6 +207,37 @@ fdm_event_del(struct fdm *fdm, int fd, int events) return false; } +bool +fdm_hook_add(struct fdm *fdm, fdm_hook_t hook, void *data) +{ +#if defined(_DEBUG) + tll_foreach(fdm->hooks, it) { + if (it->item.callback == hook) { + LOG_ERR("hook=%p already registered", hook); + return false; + } + } +#endif + + tll_push_back(fdm->hooks, ((struct hook){hook, data})); + return true; +} + +bool +fdm_hook_del(struct fdm *fdm, fdm_hook_t hook) +{ + tll_foreach(fdm->hooks, it) { + if (it->item.callback != hook) + continue; + + tll_remove(fdm->hooks, it); + return true; + } + + LOG_WARN("hook=%p not registered, hook", hook); + return false; +} + bool fdm_poll(struct fdm *fdm) { @@ -204,6 +247,12 @@ fdm_poll(struct fdm *fdm) return false; } + tll_foreach(fdm->hooks, it) { + LOG_DBG("executing hook %p(fdm=%p, data=%p)", + it->item.callback, fdm, it->item.callback_data); + it->item.callback(fdm, it->item.callback_data); + } + struct epoll_event events[tll_length(fdm->fds)]; int r = epoll_wait(fdm->epoll_fd, events, tll_length(fdm->fds), -1); diff --git a/fdm.h b/fdm.h index 62d73ded..62f7616f 100644 --- a/fdm.h +++ b/fdm.h @@ -5,6 +5,7 @@ struct fdm; typedef bool (*fdm_handler_t)(struct fdm *fdm, int fd, int events, void *data); +typedef void (*fdm_hook_t)(struct fdm *fdm, void *data); struct fdm *fdm_init(void); void fdm_destroy(struct fdm *fdm); @@ -16,4 +17,7 @@ bool fdm_del_no_close(struct fdm *fdm, int fd); bool fdm_event_add(struct fdm *fdm, int fd, int events); bool fdm_event_del(struct fdm *fdm, int fd, int events); +bool fdm_hook_add(struct fdm *fdm, fdm_hook_t hook, void *data); +bool fdm_hook_del(struct fdm *fdm, fdm_hook_t hook); + bool fdm_poll(struct fdm *fdm); diff --git a/main.c b/main.c index ec44b8b3..e418b584 100644 --- a/main.c +++ b/main.c @@ -19,6 +19,7 @@ #include "config.h" #include "fdm.h" +#include "render.h" #include "server.h" #include "shm.h" #include "terminal.h" @@ -218,6 +219,7 @@ main(int argc, char *const *argv) struct fdm *fdm = NULL; struct wayland *wayl = NULL; + struct renderer *renderer = NULL; struct terminal *term = NULL; struct server *server = NULL; struct shutdown_context shutdown_ctx = {.term = &term, .exit_code = EXIT_FAILURE}; @@ -238,6 +240,9 @@ main(int argc, char *const *argv) if ((wayl = wayl_init(&conf, fdm)) == NULL) goto out; + if ((renderer = render_init(fdm, wayl)) == NULL) + goto out; + if (!as_server && (term = term_init( &conf, fdm, wayl, conf.term, "foot", cwd, argc, argv, &term_shutdown_cb, &shutdown_ctx)) == NULL) { @@ -276,6 +281,7 @@ out: term_destroy(term); shm_fini(); + render_destroy(renderer); wayl_destroy(wayl); fdm_destroy(fdm); diff --git a/render.c b/render.c index d97da212..1f58fd05 100644 --- a/render.c +++ b/render.c @@ -22,6 +22,11 @@ #define min(x, y) ((x) < (y) ? (x) : (y)) #define max(x, y) ((x) > (y) ? (x) : (y)) +struct renderer { + struct fdm *fdm; + struct wayland *wayl; +}; + static struct { size_t total; size_t zero; /* commits presented in less than one frame interval */ @@ -29,6 +34,56 @@ static struct { size_t two; /* commits presented in two or more frame intervals */ } presentation_statistics = {0}; +static void +fdm_hook_refresh_pending_terminals(struct fdm *fdm, void *data) +{ + struct renderer *renderer = data; + tll_foreach(renderer->wayl->terms, it) { + struct terminal *term = it->item; + + if (!term->render.refresh_needed) + continue; + + assert(term->window->is_configured); + term->render.refresh_needed = false; + + if (term->window->frame_callback == NULL) { + LOG_INFO("rendering immediately"); + grid_render(term); + } else { + LOG_INFO("setting pending"); + term->render.pending = true; + } + } +} + +struct renderer * +render_init(struct fdm *fdm, struct wayland *wayl) +{ + struct renderer *renderer = calloc(1, sizeof(*renderer)); + *renderer = (struct renderer) { + .fdm = fdm, + .wayl = wayl, + }; + + if (!fdm_hook_add(fdm, &fdm_hook_refresh_pending_terminals, renderer)) { + LOG_ERR("failed to register FDM hook"); + free(renderer); + return NULL; + } + + return renderer; +} + +void +render_destroy(struct renderer *renderer) +{ + if (renderer == NULL) + return; + + fdm_hook_del(renderer->fdm, &fdm_hook_refresh_pending_terminals); +} + static void __attribute__((destructor)) log_presentation_statistics(void) { @@ -156,7 +211,7 @@ static const struct wp_presentation_feedback_listener presentation_feedback_list .discarded = &discarded, }; -struct font * +static struct font * attrs_to_font(const struct terminal *term, const struct attributes *attrs) { int idx = attrs->italic << 1 | attrs->bold; @@ -1079,10 +1134,5 @@ render_set_title(struct terminal *term, const char *_title) void render_refresh(struct terminal *term) { - assert(term->window->is_configured); - - if (term->window->frame_callback == NULL) - grid_render(term); - else - term->render.pending = true; + term->render.refresh_needed = true; } diff --git a/render.h b/render.h index ad6b6578..74d9b456 100644 --- a/render.h +++ b/render.h @@ -1,9 +1,12 @@ #pragma once #include "terminal.h" +#include "fdm.h" +#include "wayland.h" -struct font *attrs_to_font( - const struct terminal *term, const struct attributes *attrs); +struct renderer; +struct renderer *render_init(struct fdm *fdm, struct wayland *wayl); +void render_destroy(struct renderer *renderer); void grid_render(struct terminal *term); void render_resize(struct terminal *term, int width, int height); diff --git a/terminal.h b/terminal.h index 9d28ff41..e12a914e 100644 --- a/terminal.h +++ b/terminal.h @@ -283,8 +283,10 @@ struct terminal { bool visual_focus; struct { - int scrollback_lines; + bool refresh_needed; /* Terminal needs to be re-rendered, as soon-as-possible */ + int scrollback_lines; /* Number of scrollback lines, from conf (TODO: move out from render struct?) */ + /* Render threads + synchronization primitives */ struct { size_t count; sem_t start; @@ -303,7 +305,7 @@ struct terminal { struct cell *cell; /* For easy access to content */ } last_cursor; - bool pending; + bool pending; /* Need to re-render again, after next frame-callback */ struct buffer *last_buf; /* Buffer we rendered to last time */ bool was_flashing; /* Flash was active last time we rendered */ bool was_searching;