diff options
Diffstat (limited to 'src/libsystemd-terminal/grdev.c')
-rw-r--r-- | src/libsystemd-terminal/grdev.c | 1359 |
1 files changed, 0 insertions, 1359 deletions
diff --git a/src/libsystemd-terminal/grdev.c b/src/libsystemd-terminal/grdev.c deleted file mode 100644 index 71f0bd31e7..0000000000 --- a/src/libsystemd-terminal/grdev.c +++ /dev/null @@ -1,1359 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com> - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <libudev.h> -#include <stdbool.h> -#include <stdlib.h> -#include "sd-bus.h" -#include "sd-event.h" -#include "hashmap.h" -#include "login-util.h" -#include "macro.h" -#include "util.h" -#include "grdev.h" -#include "grdev-internal.h" - -static void pipe_enable(grdev_pipe *pipe); -static void pipe_disable(grdev_pipe *pipe); -static void card_modified(grdev_card *card); -static void session_frame(grdev_session *session, grdev_display *display); - -/* - * Displays - */ - -static inline grdev_tile *tile_leftmost(grdev_tile *tile) { - if (!tile) - return NULL; - - while (tile->type == GRDEV_TILE_NODE && tile->node.child_list) - tile = tile->node.child_list; - - return tile; -} - -#define TILE_FOREACH(_root, _i) \ - 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->children_by_node_next) ? : _i->parent), true); _i = _next) - -static void tile_link(grdev_tile *tile, grdev_tile *parent) { - grdev_display *display; - grdev_tile *t; - - assert(tile); - assert(!tile->parent); - assert(!tile->display); - assert(parent); - assert(parent->type == GRDEV_TILE_NODE); - - display = parent->display; - - assert(!display || !display->enabled); - - ++parent->node.n_children; - LIST_PREPEND(children_by_node, parent->node.child_list, tile); - tile->parent = parent; - - if (display) { - display->modified = true; - TILE_FOREACH(tile, t) { - t->display = display; - if (t->type == GRDEV_TILE_LEAF) { - ++display->n_leafs; - if (display->enabled) - pipe_enable(t->leaf.pipe); - } - } - } -} - -static void tile_unlink(grdev_tile *tile) { - grdev_tile *parent, *t; - grdev_display *display; - - assert(tile); - - display = tile->display; - parent = tile->parent; - if (!parent) { - assert(!display); - return; - } - - assert(parent->type == GRDEV_TILE_NODE); - assert(parent->display == display); - assert(parent->node.n_children > 0); - - --parent->node.n_children; - LIST_REMOVE(children_by_node, parent->node.child_list, tile); - tile->parent = NULL; - - if (display) { - display->modified = true; - TILE_FOREACH(tile, t) { - t->display = NULL; - if (t->type == GRDEV_TILE_LEAF) { - --display->n_leafs; - t->leaf.pipe->cache = NULL; - pipe_disable(t->leaf.pipe); - } - } - } - - /* Tile trees are driven by leafs. Internal nodes have no owner, thus, - * 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 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_children < 1 && (parent->parent || !parent->display)) - grdev_tile_free(parent); -} - -static int tile_new(grdev_tile **out) { - _cleanup_(grdev_tile_freep) grdev_tile *tile = NULL; - - assert(out); - - tile = new0(grdev_tile, 1); - if (!tile) - return -ENOMEM; - - tile->type = (unsigned)-1; - - *out = tile; - tile = NULL; - return 0; -} - -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); - - r = tile_new(&tile); - if (r < 0) - return r; - - tile->type = GRDEV_TILE_LEAF; - tile->leaf.pipe = pipe; - - if (out) - *out = tile; - tile = NULL; - return 0; -} - -int grdev_tile_new_node(grdev_tile **out) { - _cleanup_(grdev_tile_freep) grdev_tile *tile = NULL; - int r; - - assert_return(out, -EINVAL); - - r = tile_new(&tile); - if (r < 0) - return r; - - tile->type = GRDEV_TILE_NODE; - - *out = tile; - tile = NULL; - return 0; -} - -grdev_tile *grdev_tile_free(grdev_tile *tile) { - if (!tile) - return NULL; - - tile_unlink(tile); - - switch (tile->type) { - case GRDEV_TILE_LEAF: - assert(!tile->parent); - assert(!tile->display); - assert(tile->leaf.pipe); - - break; - case GRDEV_TILE_NODE: - assert(!tile->parent); - assert(!tile->display); - assert(tile->node.n_children == 0); - - break; - } - - free(tile); - - return NULL; -} - -grdev_display *grdev_find_display(grdev_session *session, const char *name) { - assert_return(session, NULL); - assert_return(name, NULL); - - return hashmap_get(session->display_map, name); -} - -int grdev_display_new(grdev_display **out, grdev_session *session, const char *name) { - _cleanup_(grdev_display_freep) grdev_display *display = NULL; - int r; - - assert(session); - assert(name); - - display = new0(grdev_display, 1); - if (!display) - return -ENOMEM; - - display->session = session; - - display->name = strdup(name); - if (!display->name) - return -ENOMEM; - - r = grdev_tile_new_node(&display->tile); - if (r < 0) - return r; - - display->tile->display = display; - - r = hashmap_put(session->display_map, display->name, display); - if (r < 0) - return r; - - if (out) - *out = display; - display = NULL; - return 0; -} - -grdev_display *grdev_display_free(grdev_display *display) { - if (!display) - return NULL; - - assert(!display->public); - assert(!display->enabled); - assert(!display->modified); - assert(display->n_leafs == 0); - assert(display->n_pipes == 0); - - if (display->name) - hashmap_remove_value(display->session->display_map, display->name, display); - - if (display->tile) { - display->tile->display = NULL; - grdev_tile_free(display->tile); - } - - free(display->pipes); - free(display->name); - free(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; -} - -void grdev_display_enable(grdev_display *display) { - grdev_tile *t; - - assert(display); - - if (!display->enabled) { - display->enabled = true; - TILE_FOREACH(display->tile, t) - if (t->type == GRDEV_TILE_LEAF) - pipe_enable(t->leaf.pipe); - } -} - -void grdev_display_disable(grdev_display *display) { - grdev_tile *t; - - assert(display); - - if (display->enabled) { - display->enabled = false; - TILE_FOREACH(display->tile, t) - if (t->type == GRDEV_TILE_LEAF) - pipe_disable(t->leaf.pipe); - } -} - -const grdev_display_target *grdev_display_next_target(grdev_display *display, const grdev_display_target *prev) { - grdev_display_cache *cache; - size_t idx; - - assert_return(display, NULL); - assert_return(!display->modified, NULL); - assert_return(display->enabled, NULL); - - if (prev) { - cache = container_of(prev, grdev_display_cache, target); - - assert(cache->pipe); - assert(cache->pipe->tile->display == display); - assert(display->pipes >= cache); - - idx = cache - display->pipes + 1; - } else { - idx = 0; - } - - for (cache = display->pipes + idx; idx < display->n_pipes; ++idx, ++cache) { - grdev_display_target *target; - grdev_pipe *pipe; - grdev_fb *fb; - - pipe = cache->pipe; - target = &cache->target; - - if (!pipe->running || !pipe->enabled) - continue; - - /* find suitable back-buffer */ - if (!pipe->back) { - if (!pipe->vtable->target) - continue; - if (!(fb = pipe->vtable->target(pipe))) - continue; - - assert(fb == pipe->back); - } - - 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) { - grdev_display_cache *cache; - - assert(display); - assert(!display->modified); - assert(display->enabled); - assert(target); - - cache = container_of(target, grdev_display_cache, target); - - assert(cache->pipe); - assert(cache->pipe->tile->display == display); - - cache->pipe->flip = true; -} - -static void display_cache_apply(grdev_display_cache *c, grdev_tile *l) { - uint32_t x, y, width, height; - grdev_display_target *t; - - assert(c); - assert(l); - assert(l->cache_w >= c->target.width + c->target.x); - assert(l->cache_h >= c->target.height + c->target.y); - - t = &c->target; - - /* rotate child */ - - t->rotate = (t->rotate + l->rotate) & 0x3; - - x = t->x; - y = t->y; - width = t->width; - height = t->height; - - switch (l->rotate) { - case GRDEV_ROTATE_0: - break; - case GRDEV_ROTATE_90: - t->x = l->cache_h - (height + y); - t->y = x; - t->width = height; - t->height = width; - break; - case GRDEV_ROTATE_180: - t->x = l->cache_w - (width + x); - t->y = l->cache_h - (height + y); - break; - case GRDEV_ROTATE_270: - t->x = y; - t->y = l->cache_w - (width + x); - t->width = height; - t->height = width; - break; - } - - /* flip child */ - - t->flip ^= l->flip; - - if (l->flip & GRDEV_FLIP_HORIZONTAL) - t->x = l->cache_w - (t->width + t->x); - if (l->flip & GRDEV_FLIP_VERTICAL) - t->y = l->cache_h - (t->height + t->y); - - /* move child */ - - t->x += l->x; - t->y += l->y; -} - -static void display_cache_targets(grdev_display *display) { - grdev_display_cache *c; - grdev_tile *tile; - - assert(display); - - /* depth-first with children before parent */ - for (tile = tile_leftmost(display->tile); - tile; - tile = tile_leftmost(tile->children_by_node_next) ? : tile->parent) { - if (tile->type == GRDEV_TILE_LEAF) { - grdev_pipe *p; - - /* We're at a leaf and no parent has been cached, yet. - * Copy the pipe information into the target cache and - * update our global pipe-caches if required. */ - - assert(tile->leaf.pipe); - assert(display->n_pipes + 1 <= display->max_pipes); - - p = tile->leaf.pipe; - c = &display->pipes[display->n_pipes++]; - - zero(*c); - c->pipe = p; - c->pipe->cache = c; - c->target.width = p->width; - c->target.height = p->height; - tile->cache_w = p->width; - tile->cache_h = p->height; - - /* all new tiles are incomplete due to geometry changes */ - c->incomplete = true; - - display_cache_apply(c, tile); - } else { - grdev_tile *child, *l; - - /* 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. */ - - tile->cache_w = 0; - tile->cache_h = 0; - - 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) - tile->cache_h = child->y + child->cache_h; - } - - assert(tile->cache_w > 0); - assert(tile->cache_h > 0); - - TILE_FOREACH(tile, l) - if (l->type == GRDEV_TILE_LEAF) - display_cache_apply(l->leaf.pipe->cache, tile); - } - } -} - -static bool display_cache(grdev_display *display) { - grdev_tile *tile; - size_t n; - void *t; - int r; - - assert(display); - - if (!display->modified) - return false; - - display->modified = false; - display->framed = false; - display->n_pipes = 0; - display->width = 0; - display->height = 0; - - if (display->n_leafs < 1) - return false; - - TILE_FOREACH(display->tile, tile) - if (tile->type == GRDEV_TILE_LEAF) - tile->leaf.pipe->cache = NULL; - - if (display->n_leafs > display->max_pipes) { - n = ALIGN_POWER2(display->n_leafs); - if (!n) { - r = -ENOMEM; - goto out; - } - - t = realloc_multiply(display->pipes, sizeof(*display->pipes), n); - if (!t) { - r = -ENOMEM; - goto out; - } - - display->pipes = t; - display->max_pipes = n; - } - - display_cache_targets(display); - display->width = display->tile->cache_w; - display->height = display->tile->cache_h; - - r = 0; - -out: - if (r < 0) - log_debug_errno(r, "grdev: %s/%s: cannot cache pipes: %m", - display->session->name, display->name); - return true; -} - -/* - * Pipes - */ - -grdev_pipe *grdev_find_pipe(grdev_card *card, const char *name) { - assert_return(card, NULL); - assert_return(name, NULL); - - 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; - - assert_return(pipe, -EINVAL); - assert_return(pipe->vtable, -EINVAL); - assert_return(pipe->vtable->free, -EINVAL); - assert_return(pipe->card, -EINVAL); - assert_return(pipe->card->session, -EINVAL); - 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); - - pipe->name = strdup(name); - if (!pipe->name) - return -ENOMEM; - - if (n_fbs > 0) { - pipe->fbs = new0(grdev_fb*, n_fbs); - if (!pipe->fbs) - return -ENOMEM; - - pipe->max_fbs = n_fbs; - } - - r = grdev_tile_new_leaf(&pipe->tile, pipe); - 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; - - card_modified(pipe->card); - return 0; -} - -grdev_pipe *grdev_pipe_free(grdev_pipe *pipe) { - grdev_pipe tmp; - - if (!pipe) - return NULL; - - assert(pipe->card); - assert(pipe->vtable); - assert(pipe->vtable->free); - - if (pipe->name) - hashmap_remove_value(pipe->card->pipe_map, pipe->name, pipe); - if (pipe->tile) - tile_unlink(pipe->tile); - - assert(!pipe->cache); - - 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); - free(tmp.name); - - return NULL; -} - -static void pipe_enable(grdev_pipe *pipe) { - assert(pipe); - - if (!pipe->enabled) { - pipe->enabled = true; - if (pipe->vtable->enable) - pipe->vtable->enable(pipe); - } -} - -static void pipe_disable(grdev_pipe *pipe) { - assert(pipe); - - if (pipe->enabled) { - pipe->enabled = false; - if (pipe->vtable->disable) - pipe->vtable->disable(pipe); - } -} - -void grdev_pipe_ready(grdev_pipe *pipe, bool running) { - assert(pipe); - - /* 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 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. */ - - if (pipe->running == running) - return; - - pipe->running = running; - - /* runtime events for unused pipes are not interesting */ - if (pipe->cache && pipe->enabled) { - grdev_display *display = pipe->tile->display; - - assert(display); - - if (running) - session_frame(display->session, display); - else - pipe->cache->incomplete = true; - } -} - -void grdev_pipe_frame(grdev_pipe *pipe) { - grdev_display *display; - - assert(pipe); - - /* if pipe is unused, ignore any frame events */ - if (!pipe->cache || !pipe->enabled) - return; - - display = pipe->tile->display; - assert(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); -} - -/* - * Cards - */ - -grdev_card *grdev_find_card(grdev_session *session, const char *name) { - assert_return(session, NULL); - assert_return(name, NULL); - - return hashmap_get(session->card_map, name); -} - -int grdev_card_add(grdev_card *card, const char *name) { - int r; - - assert_return(card, -EINVAL); - assert_return(card->vtable, -EINVAL); - assert_return(card->vtable->free, -EINVAL); - assert_return(card->session, -EINVAL); - assert_return(name, -EINVAL); - - card->name = strdup(name); - if (!card->name) - return -ENOMEM; - - card->pipe_map = hashmap_new(&string_hash_ops); - if (!card->pipe_map) - return -ENOMEM; - - r = hashmap_put(card->session->card_map, card->name, card); - if (r < 0) - return r; - - return 0; -} - -grdev_card *grdev_card_free(grdev_card *card) { - grdev_card tmp; - - if (!card) - return NULL; - - assert(!card->enabled); - assert(card->vtable); - assert(card->vtable->free); - - if (card->name) - hashmap_remove_value(card->session->card_map, card->name, card); - - tmp = *card; - card->vtable->free(card); - - assert(hashmap_size(tmp.pipe_map) == 0); - - hashmap_free(tmp.pipe_map); - free(tmp.name); - - return NULL; -} - -static void card_modified(grdev_card *card) { - assert(card); - assert(card->session->n_pins > 0); - - card->modified = true; -} - -static void grdev_card_enable(grdev_card *card) { - assert(card); - - if (!card->enabled) { - card->enabled = true; - if (card->vtable->enable) - card->vtable->enable(card); - } -} - -static void grdev_card_disable(grdev_card *card) { - assert(card); - - if (card->enabled) { - card->enabled = false; - if (card->vtable->disable) - card->vtable->disable(card); - } -} - -/* - * Sessions - */ - -static void session_raise(grdev_session *session, grdev_event *event) { - session->event_fn(session, session->userdata, event); -} - -static void session_raise_display_add(grdev_session *session, grdev_display *display) { - grdev_event event = { - .type = GRDEV_EVENT_DISPLAY_ADD, - .display_add = { - .display = display, - }, - }; - - session_raise(session, &event); -} - -static void session_raise_display_remove(grdev_session *session, grdev_display *display) { - grdev_event event = { - .type = GRDEV_EVENT_DISPLAY_REMOVE, - .display_remove = { - .display = display, - }, - }; - - session_raise(session, &event); -} - -static void session_raise_display_change(grdev_session *session, grdev_display *display) { - grdev_event event = { - .type = GRDEV_EVENT_DISPLAY_CHANGE, - .display_change = { - .display = display, - }, - }; - - session_raise(session, &event); -} - -static void session_raise_display_frame(grdev_session *session, grdev_display *display) { - grdev_event event = { - .type = GRDEV_EVENT_DISPLAY_FRAME, - .display_frame = { - .display = display, - }, - }; - - session_raise(session, &event); -} - -static void session_add_card(grdev_session *session, grdev_card *card) { - assert(session); - assert(card); - - log_debug("grdev: %s: add card '%s'", session->name, card->name); - - /* Cards are not exposed to users, but managed internally. Cards are - * enabled if the session is enabled, and will track that state. The - * backend can probe the card at any time, but only if enabled. It - * will then add pipes according to hardware state. - * That is, the card may create pipes as soon as we enable it here. */ - - if (session->enabled) - grdev_card_enable(card); -} - -static void session_remove_card(grdev_session *session, grdev_card *card) { - assert(session); - assert(card); - - log_debug("grdev: %s: remove card '%s'", session->name, card->name); - - /* As cards are not exposed, it can never be accessed by outside - * users and we can simply remove it. Disabling the card does not - * necessarily drop all pipes of the card. This is usually deferred - * to card destruction (as pipes are cached as long as FDs remain - * open). Therefore, the card destruction might cause pipes, and thus - * visible displays, to be removed. */ - - grdev_card_disable(card); - grdev_card_free(card); -} - -static void session_add_display(grdev_session *session, grdev_display *display) { - assert(session); - assert(display); - assert(!display->enabled); - - log_debug("grdev: %s: add display '%s'", session->name, display->name); - - /* Displays are the main entity for public API users. We create them - * independent of card backends and they wrap any underlying display - * architecture. Displays are public at all times, thus, may be entered - * by outside users at any time. */ - - display->public = true; - session_raise_display_add(session, display); -} - -static void session_remove_display(grdev_session *session, grdev_display *display) { - assert(session); - assert(display); - - log_debug("grdev: %s: remove display '%s'", session->name, display->name); - - /* Displays are public, so we have to be careful when removing them. - * We first tell users about their removal, disable them and then drop - * them. We now, after the notification, no external access will - * happen. Therefore, we can release the tiles afterwards safely. */ - - if (display->public) { - display->public = false; - session_raise_display_remove(session, display); - } - - grdev_display_disable(display); - grdev_display_free(display); -} - -static void session_change_display(grdev_session *session, grdev_display *display) { - bool changed; - - assert(session); - assert(display); - - changed = display_cache(display); - - if (display->n_leafs == 0) { - session_remove_display(session, display); - } else if (!display->public) { - session_add_display(session, display); - session_frame(session, display); - } else if (changed) { - session_raise_display_change(session, display); - session_frame(session, display); - } else if (display->framed) { - session_frame(session, display); - } -} - -static void session_frame(grdev_session *session, grdev_display *display) { - assert(session); - assert(display); - - display->framed = false; - - if (!display->enabled || !session->enabled) - return; - - if (session->n_pins > 0) - display->framed = true; - else - session_raise_display_frame(session, display); -} - -int grdev_session_new(grdev_session **out, - grdev_context *context, - unsigned int flags, - const char *name, - grdev_event_fn event_fn, - void *userdata) { - _cleanup_(grdev_session_freep) grdev_session *session = NULL; - int r; - - assert(out); - assert(context); - assert(name); - assert(event_fn); - assert_return(session_id_valid(name) == !(flags & GRDEV_SESSION_CUSTOM), -EINVAL); - assert_return(!(flags & GRDEV_SESSION_CUSTOM) || !(flags & GRDEV_SESSION_MANAGED), -EINVAL); - assert_return(!(flags & GRDEV_SESSION_MANAGED) || context->sysbus, -EINVAL); - - session = new0(grdev_session, 1); - if (!session) - return -ENOMEM; - - session->context = grdev_context_ref(context); - session->custom = flags & GRDEV_SESSION_CUSTOM; - session->managed = flags & GRDEV_SESSION_MANAGED; - session->event_fn = event_fn; - session->userdata = userdata; - - session->name = strdup(name); - if (!session->name) - return -ENOMEM; - - if (session->managed) { - r = sd_bus_path_encode("/org/freedesktop/login1/session", - session->name, &session->path); - if (r < 0) - return r; - } - - session->card_map = hashmap_new(&string_hash_ops); - if (!session->card_map) - return -ENOMEM; - - session->display_map = hashmap_new(&string_hash_ops); - if (!session->display_map) - return -ENOMEM; - - r = hashmap_put(context->session_map, session->name, session); - if (r < 0) - return r; - - *out = session; - session = NULL; - return 0; -} - -grdev_session *grdev_session_free(grdev_session *session) { - grdev_card *card; - - if (!session) - return NULL; - - grdev_session_disable(session); - - while ((card = hashmap_first(session->card_map))) - session_remove_card(session, card); - - assert(hashmap_size(session->display_map) == 0); - - if (session->name) - hashmap_remove_value(session->context->session_map, session->name, session); - - hashmap_free(session->display_map); - hashmap_free(session->card_map); - session->context = grdev_context_unref(session->context); - free(session->path); - free(session->name); - free(session); - - return NULL; -} - -bool grdev_session_is_enabled(grdev_session *session) { - return session && session->enabled; -} - -void grdev_session_enable(grdev_session *session) { - grdev_card *card; - Iterator iter; - - assert(session); - - if (!session->enabled) { - session->enabled = true; - HASHMAP_FOREACH(card, session->card_map, iter) - grdev_card_enable(card); - } -} - -void grdev_session_disable(grdev_session *session) { - grdev_card *card; - Iterator iter; - - assert(session); - - if (session->enabled) { - session->enabled = false; - HASHMAP_FOREACH(card, session->card_map, iter) - grdev_card_disable(card); - } -} - -void grdev_session_commit(grdev_session *session) { - grdev_card *card; - Iterator iter; - - assert(session); - - if (!session->enabled) - return; - - HASHMAP_FOREACH(card, session->card_map, iter) - if (card->vtable->commit) - card->vtable->commit(card); -} - -void grdev_session_restore(grdev_session *session) { - grdev_card *card; - Iterator iter; - - assert(session); - - if (!session->enabled) - return; - - HASHMAP_FOREACH(card, session->card_map, iter) - if (card->vtable->restore) - card->vtable->restore(card); -} - -void grdev_session_add_drm(grdev_session *session, struct udev_device *ud) { - grdev_card *card; - dev_t devnum; - int r; - - assert(session); - assert(ud); - - devnum = udev_device_get_devnum(ud); - if (devnum == 0) - return grdev_session_hotplug_drm(session, ud); - - card = grdev_find_drm_card(session, devnum); - if (card) - return; - - r = grdev_drm_card_new(&card, session, ud); - if (r < 0) { - log_debug_errno(r, "grdev: %s: cannot add DRM device for %s: %m", - session->name, udev_device_get_syspath(ud)); - return; - } - - session_add_card(session, card); -} - -void grdev_session_remove_drm(grdev_session *session, struct udev_device *ud) { - grdev_card *card; - dev_t devnum; - - assert(session); - assert(ud); - - devnum = udev_device_get_devnum(ud); - if (devnum == 0) - return grdev_session_hotplug_drm(session, ud); - - card = grdev_find_drm_card(session, devnum); - if (!card) - return; - - session_remove_card(session, card); -} - -void grdev_session_hotplug_drm(grdev_session *session, struct udev_device *ud) { - grdev_card *card = NULL; - struct udev_device *p; - dev_t devnum; - - assert(session); - assert(ud); - - for (p = ud; p; p = udev_device_get_parent_with_subsystem_devtype(p, "drm", NULL)) { - devnum = udev_device_get_devnum(ud); - if (devnum == 0) - continue; - - card = grdev_find_drm_card(session, devnum); - if (card) - break; - } - - if (!card) - return; - - grdev_drm_card_hotplug(card, ud); -} - -static void session_configure(grdev_session *session) { - grdev_display *display; - grdev_tile *tile; - grdev_card *card; - grdev_pipe *pipe; - Iterator i, j; - int r; - - assert(session); - - /* - * Whenever backends add or remove pipes, we set session->modified and - * require them to pin the session while modifying it. On release, we - * reconfigure the device and re-assign displays to all modified pipes. - * - * So far, we configure each pipe as a separate display. We do not - * support user-configuration, nor have we gotten any reports from - * users with multi-pipe monitors (4k on DP-1.2 MST and so on). Until - * we get reports, we keep the logic to a minimum. - */ - - /* create new displays for all unconfigured pipes */ - HASHMAP_FOREACH(card, session->card_map, i) { - if (!card->modified) - continue; - - card->modified = false; - - HASHMAP_FOREACH(pipe, card->pipe_map, j) { - tile = pipe->tile; - if (tile->display) - continue; - - assert(!tile->parent); - - display = grdev_find_display(session, pipe->name); - if (display && display->tile) { - log_debug("grdev: %s/%s: occupied display for pipe %s", - session->name, card->name, pipe->name); - continue; - } else if (!display) { - r = grdev_display_new(&display, session, pipe->name); - if (r < 0) { - log_debug_errno(r, "grdev: %s/%s: cannot create display for pipe %s: %m", - session->name, card->name, pipe->name); - continue; - } - } - - tile_link(pipe->tile, display->tile); - } - } - - /* update displays */ - HASHMAP_FOREACH(display, session->display_map, i) - session_change_display(session, display); -} - -grdev_session *grdev_session_pin(grdev_session *session) { - assert(session); - - ++session->n_pins; - return session; -} - -grdev_session *grdev_session_unpin(grdev_session *session) { - if (!session) - return NULL; - - assert(session->n_pins > 0); - - if (--session->n_pins == 0) - session_configure(session); - - return NULL; -} - -/* - * Contexts - */ - -int grdev_context_new(grdev_context **out, sd_event *event, sd_bus *sysbus) { - _cleanup_(grdev_context_unrefp) grdev_context *context = NULL; - - assert_return(out, -EINVAL); - assert_return(event, -EINVAL); - - context = new0(grdev_context, 1); - if (!context) - return -ENOMEM; - - context->ref = 1; - context->event = sd_event_ref(event); - - if (sysbus) - context->sysbus = sd_bus_ref(sysbus); - - context->session_map = hashmap_new(&string_hash_ops); - if (!context->session_map) - return -ENOMEM; - - *out = context; - context = NULL; - return 0; -} - -static void context_cleanup(grdev_context *context) { - assert(hashmap_size(context->session_map) == 0); - - hashmap_free(context->session_map); - context->sysbus = sd_bus_unref(context->sysbus); - context->event = sd_event_unref(context->event); - free(context); -} - -grdev_context *grdev_context_ref(grdev_context *context) { - assert_return(context, NULL); - assert_return(context->ref > 0, NULL); - - ++context->ref; - return context; -} - -grdev_context *grdev_context_unref(grdev_context *context) { - if (!context) - return NULL; - - assert_return(context->ref > 0, NULL); - - if (--context->ref == 0) - context_cleanup(context); - - return NULL; -} |