X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Flibsystemd-terminal%2Fgrdev.c;h=feed579295e17206ee00889c7265559e3905772d;hb=e0952d9d021234e79f3a70f33a9e5d201872a417;hp=bb89ee7170c9cc9ac886870009f8abcd6a461bdb;hpb=a3eabec96b872bbf581c9bfa81ecc9c2819b8de8;p=elogind.git
diff --git a/src/libsystemd-terminal/grdev.c b/src/libsystemd-terminal/grdev.c
index bb89ee717..feed57929 100644
--- a/src/libsystemd-terminal/grdev.c
+++ b/src/libsystemd-terminal/grdev.c
@@ -19,19 +19,16 @@
along with systemd; If not, see .
***/
-#include
#include
#include
#include
#include
#include
-#include
#include "grdev.h"
#include "grdev-internal.h"
#include "hashmap.h"
#include "login-shared.h"
#include "macro.h"
-#include "udev-util.h"
#include "util.h"
static void pipe_enable(grdev_pipe *pipe);
@@ -54,10 +51,10 @@ static inline grdev_tile *tile_leftmost(grdev_tile *tile) {
}
#define TILE_FOREACH(_root, _i) \
- for (_i = tile_leftmost(_root); _i; _i = tile_leftmost(_i->childs_by_node_next) ? : _i->parent)
+ for (_i = tile_leftmost(_root); _i; _i = tile_leftmost(_i->children_by_node_next) ? : _i->parent)
#define TILE_FOREACH_SAFE(_root, _i, _next) \
- for (_i = tile_leftmost(_root); _i && ((_next = tile_leftmost(_i->childs_by_node_next) ? : _i->parent), true); _i = _next)
+ for (_i = tile_leftmost(_root); _i && ((_next = tile_leftmost(_i->children_by_node_next) ? : _i->parent), true); _i = _next)
static void tile_link(grdev_tile *tile, grdev_tile *parent) {
grdev_display *display;
@@ -73,8 +70,8 @@ static void tile_link(grdev_tile *tile, grdev_tile *parent) {
assert(!display || !display->enabled);
- ++parent->node.n_childs;
- LIST_PREPEND(childs_by_node, parent->node.child_list, tile);
+ ++parent->node.n_children;
+ LIST_PREPEND(children_by_node, parent->node.child_list, tile);
tile->parent = parent;
if (display) {
@@ -105,10 +102,10 @@ static void tile_unlink(grdev_tile *tile) {
assert(parent->type == GRDEV_TILE_NODE);
assert(parent->display == display);
- assert(parent->node.n_childs > 0);
+ assert(parent->node.n_children > 0);
- --parent->node.n_childs;
- LIST_REMOVE(childs_by_node, parent->node.child_list, tile);
+ --parent->node.n_children;
+ LIST_REMOVE(children_by_node, parent->node.child_list, tile);
tile->parent = NULL;
if (display) {
@@ -127,14 +124,14 @@ static void tile_unlink(grdev_tile *tile) {
* we must take care to not leave them around. Therefore, whenever we
* unlink any part of a tree, we also destroy the parent, in case it's
* now stale.
- * Parents are stale if they have no childs and either have no display
+ * Parents are stale if they have no children and either have no display
* or if they are intermediate nodes (i.e, they have a parent).
* This means, you can easily create trees, but you can never partially
* move or destruct them so far. They're always reduced to minimal form
* if you cut them. This might change later, but so far we didn't need
* partial destruction or the ability to move whole trees. */
- if (parent->node.n_childs < 1 && (parent->parent || !parent->display))
+ if (parent->node.n_children < 1 && (parent->parent || !parent->display))
grdev_tile_free(parent);
}
@@ -158,6 +155,7 @@ int grdev_tile_new_leaf(grdev_tile **out, grdev_pipe *pipe) {
_cleanup_(grdev_tile_freep) grdev_tile *tile = NULL;
int r;
+ assert_return(out, -EINVAL);
assert_return(pipe, -EINVAL);
assert_return(!pipe->tile, -EINVAL);
@@ -207,7 +205,7 @@ grdev_tile *grdev_tile_free(grdev_tile *tile) {
case GRDEV_TILE_NODE:
assert(!tile->parent);
assert(!tile->display);
- assert(tile->node.n_childs == 0);
+ assert(tile->node.n_children == 0);
break;
}
@@ -282,6 +280,36 @@ grdev_display *grdev_display_free(grdev_display *display) {
return NULL;
}
+void grdev_display_set_userdata(grdev_display *display, void *userdata) {
+ assert(display);
+
+ display->userdata = userdata;
+}
+
+void *grdev_display_get_userdata(grdev_display *display) {
+ assert_return(display, NULL);
+
+ return display->userdata;
+}
+
+const char *grdev_display_get_name(grdev_display *display) {
+ assert_return(display, NULL);
+
+ return display->name;
+}
+
+uint32_t grdev_display_get_width(grdev_display *display) {
+ assert_return(display, 0);
+
+ return display->width;
+}
+
+uint32_t grdev_display_get_height(grdev_display *display) {
+ assert_return(display, 0);
+
+ return display->height;
+}
+
bool grdev_display_is_enabled(grdev_display *display) {
return display && display->enabled;
}
@@ -312,7 +340,7 @@ void grdev_display_disable(grdev_display *display) {
}
}
-const grdev_display_target *grdev_display_next_target(grdev_display *display, const grdev_display_target *prev, uint64_t minage) {
+const grdev_display_target *grdev_display_next_target(grdev_display *display, const grdev_display_target *prev) {
grdev_display_cache *cache;
size_t idx;
@@ -327,7 +355,7 @@ const grdev_display_target *grdev_display_next_target(grdev_display *display, co
assert(cache->pipe->tile->display == display);
assert(display->pipes >= cache);
- idx = (cache - display->pipes) / sizeof(*cache) + 1;
+ idx = cache - display->pipes + 1;
} else {
idx = 0;
}
@@ -343,52 +371,38 @@ const grdev_display_target *grdev_display_next_target(grdev_display *display, co
if (!pipe->running || !pipe->enabled)
continue;
- /* if front-buffer is up-to-date, there's nothing to do */
- if (minage > 0 && pipe->front && pipe->front->age >= minage)
- continue;
-
/* find suitable back-buffer */
- if (!(fb = pipe->back)) {
- if (!pipe->vtable->target || !(fb = pipe->vtable->target(pipe)))
+ if (!pipe->back) {
+ if (!pipe->vtable->target)
+ continue;
+ if (!(fb = pipe->vtable->target(pipe)))
continue;
- }
- /* if back-buffer is up-to-date, schedule flip */
- if (minage > 0 && fb->age >= minage) {
- grdev_display_flip_target(display, target, fb->age);
- continue;
+ assert(fb == pipe->back);
}
- /* we have an out-of-date back-buffer; return for redraw */
- target->fb = fb;
+ target->front = pipe->front;
+ target->back = pipe->back;
+
return target;
}
return NULL;
}
-void grdev_display_flip_target(grdev_display *display, const grdev_display_target *target, uint64_t age) {
+void grdev_display_flip_target(grdev_display *display, const grdev_display_target *target) {
grdev_display_cache *cache;
- size_t i;
assert(display);
assert(!display->modified);
assert(display->enabled);
assert(target);
- assert(target->fb);
cache = container_of(target, grdev_display_cache, target);
assert(cache->pipe);
assert(cache->pipe->tile->display == display);
- /* reset age of all FB on overflow */
- if (age < target->fb->age)
- for (i = 0; i < cache->pipe->max_fbs; ++i)
- if (cache->pipe->fbs[i])
- cache->pipe->fbs[i]->age = 0;
-
- ((grdev_fb*)target->fb)->age = age;
cache->pipe->flip = true;
}
@@ -454,10 +468,10 @@ static void display_cache_targets(grdev_display *display) {
assert(display);
- /* depth-first with childs before parent */
+ /* depth-first with children before parent */
for (tile = tile_leftmost(display->tile);
tile;
- tile = tile_leftmost(tile->childs_by_node_next) ? : tile->parent) {
+ tile = tile_leftmost(tile->children_by_node_next) ? : tile->parent) {
if (tile->type == GRDEV_TILE_LEAF) {
grdev_pipe *p;
@@ -486,7 +500,7 @@ static void display_cache_targets(grdev_display *display) {
} else {
grdev_tile *child, *l;
- /* We're now at a node with all it's childs already
+ /* We're now at a node with all its children already
* computed (depth-first, child before parent). We
* first need to know the size of our tile, then we
* recurse into all leafs and update their cache. */
@@ -494,7 +508,7 @@ static void display_cache_targets(grdev_display *display) {
tile->cache_w = 0;
tile->cache_h = 0;
- LIST_FOREACH(childs_by_node, child, tile->node.child_list) {
+ LIST_FOREACH(children_by_node, child, tile->node.child_list) {
if (child->x + child->cache_w > tile->cache_w)
tile->cache_w = child->x + child->cache_w;
if (child->y + child->cache_h > tile->cache_h)
@@ -553,13 +567,15 @@ static bool display_cache(grdev_display *display) {
}
display_cache_targets(display);
+ display->width = display->tile->cache_w;
+ display->height = display->tile->cache_h;
r = 0;
out:
if (r < 0)
- log_debug("grdev: %s/%s: cannot cache pipes: %s",
- display->session->name, display->name, strerror(-r));
+ log_debug_errno(r, "grdev: %s/%s: cannot cache pipes: %m",
+ display->session->name, display->name);
return true;
}
@@ -574,6 +590,13 @@ grdev_pipe *grdev_find_pipe(grdev_card *card, const char *name) {
return hashmap_get(card->pipe_map, name);
}
+static int pipe_vsync_fn(sd_event_source *src, uint64_t usec, void *userdata) {
+ grdev_pipe *pipe = userdata;
+
+ grdev_pipe_frame(pipe);
+ return 0;
+}
+
int grdev_pipe_add(grdev_pipe *pipe, const char *name, size_t n_fbs) {
int r;
@@ -585,6 +608,7 @@ int grdev_pipe_add(grdev_pipe *pipe, const char *name, size_t n_fbs) {
assert_return(!pipe->cache, -EINVAL);
assert_return(pipe->width > 0, -EINVAL);
assert_return(pipe->height > 0, -EINVAL);
+ assert_return(pipe->vrefresh > 0, -EINVAL);
assert_return(!pipe->enabled, -EINVAL);
assert_return(!pipe->running, -EINVAL);
assert_return(name, -EINVAL);
@@ -605,6 +629,20 @@ int grdev_pipe_add(grdev_pipe *pipe, const char *name, size_t n_fbs) {
if (r < 0)
return r;
+ r = sd_event_add_time(pipe->card->session->context->event,
+ &pipe->vsync_src,
+ CLOCK_MONOTONIC,
+ 0,
+ 10 * USEC_PER_MSEC,
+ pipe_vsync_fn,
+ pipe);
+ if (r < 0)
+ return r;
+
+ r = sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_OFF);
+ if (r < 0)
+ return r;
+
r = hashmap_put(pipe->card->pipe_map, pipe->name, pipe);
if (r < 0)
return r;
@@ -633,6 +671,7 @@ grdev_pipe *grdev_pipe_free(grdev_pipe *pipe) {
tmp = *pipe;
pipe->vtable->free(pipe);
+ sd_event_source_unref(tmp.vsync_src);
grdev_tile_free(tmp.tile);
card_modified(tmp.card);
free(tmp.fbs);
@@ -666,7 +705,7 @@ void grdev_pipe_ready(grdev_pipe *pipe, bool running) {
/* grdev_pipe_ready() is used by backends to notify about pipe state
* changed. If a pipe is ready, it can be fully used by us (available,
- * enabled and accessable). Backends can disable pipes at any time
+ * enabled and accessible). Backends can disable pipes at any time
* (like for async revocation), but can only enable them from parent
* context. Otherwise, we might call user-callbacks recursively. */
@@ -676,17 +715,15 @@ void grdev_pipe_ready(grdev_pipe *pipe, bool running) {
pipe->running = running;
/* runtime events for unused pipes are not interesting */
- if (pipe->cache) {
+ if (pipe->cache && pipe->enabled) {
grdev_display *display = pipe->tile->display;
assert(display);
- if (running) {
- if (pipe->enabled)
- session_frame(display->session, display);
- } else {
+ if (running)
+ session_frame(display->session, display);
+ else
pipe->cache->incomplete = true;
- }
}
}
@@ -696,14 +733,44 @@ void grdev_pipe_frame(grdev_pipe *pipe) {
assert(pipe);
/* if pipe is unused, ignore any frame events */
- if (!pipe->cache)
+ if (!pipe->cache || !pipe->enabled)
return;
display = pipe->tile->display;
assert(display);
- if (pipe->enabled)
- session_frame(display->session, display);
+ grdev_pipe_schedule(pipe, 0);
+ session_frame(display->session, display);
+}
+
+void grdev_pipe_schedule(grdev_pipe *pipe, uint64_t frames) {
+ int r;
+ uint64_t ts;
+
+ if (!frames) {
+ sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_OFF);
+ return;
+ }
+
+ r = sd_event_now(pipe->card->session->context->event, CLOCK_MONOTONIC, &ts);
+ if (r < 0)
+ goto error;
+
+ ts += frames * USEC_PER_MSEC * 1000ULL / pipe->vrefresh;
+
+ r = sd_event_source_set_time(pipe->vsync_src, ts);
+ if (r < 0)
+ goto error;
+
+ r = sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_ONESHOT);
+ if (r < 0)
+ goto error;
+
+ return;
+
+error:
+ log_debug_errno(r, "grdev: %s/%s/%s: cannot schedule vsync timer: %m",
+ pipe->card->session->name, pipe->card->name, pipe->name);
}
/*
@@ -921,14 +988,17 @@ static void session_change_display(grdev_session *session, grdev_display *displa
changed = display_cache(display);
- if (display->n_leafs == 0)
+ if (display->n_leafs == 0) {
session_remove_display(session, display);
- else if (!display->public)
+ } else if (!display->public) {
session_add_display(session, display);
- else if (changed)
+ session_frame(session, display);
+ } else if (changed) {
session_raise_display_change(session, display);
- else if (display->framed)
session_frame(session, display);
+ } else if (display->framed) {
+ session_frame(session, display);
+ }
}
static void session_frame(grdev_session *session, grdev_display *display) {
@@ -1103,8 +1173,8 @@ void grdev_session_add_drm(grdev_session *session, struct udev_device *ud) {
r = grdev_drm_card_new(&card, session, ud);
if (r < 0) {
- log_debug("grdev: %s: cannot add DRM device for %s: %s",
- session->name, udev_device_get_syspath(ud), strerror(-r));
+ log_debug_errno(r, "grdev: %s: cannot add DRM device for %s: %m",
+ session->name, udev_device_get_syspath(ud));
return;
}
@@ -1150,7 +1220,7 @@ void grdev_session_hotplug_drm(grdev_session *session, struct udev_device *ud) {
if (!card)
return;
- /* TODO: hotplug card */
+ grdev_drm_card_hotplug(card, ud);
}
static void session_configure(grdev_session *session) {
@@ -1196,8 +1266,8 @@ static void session_configure(grdev_session *session) {
} else if (!display) {
r = grdev_display_new(&display, session, pipe->name);
if (r < 0) {
- log_debug("grdev: %s/%s: cannot create display for pipe %s: %s",
- session->name, card->name, pipe->name, strerror(-r));
+ log_debug_errno(r, "grdev: %s/%s: cannot create display for pipe %s: %m",
+ session->name, card->name, pipe->name);
continue;
}
}