Zbigniew Jędrzejewski-Szmek 62fe94
From 650c5444273993f969b9cd7df9add6ab2df0414e Mon Sep 17 00:00:00 2001
Zbigniew Jędrzejewski-Szmek 62fe94
From: David Herrmann <dh.herrmann@gmail.com>
Zbigniew Jędrzejewski-Szmek 62fe94
Date: Fri, 19 Sep 2014 14:05:52 +0200
Zbigniew Jędrzejewski-Szmek 62fe94
Subject: [PATCH] terminal: add graphics interface
Zbigniew Jędrzejewski-Szmek 62fe94
Zbigniew Jędrzejewski-Szmek 62fe94
The grdev layer provides graphics-device access via the
Zbigniew Jędrzejewski-Szmek 62fe94
libsystemd-terminal library. It will be used by all terminal helpers to
Zbigniew Jędrzejewski-Szmek 62fe94
actually access display hardware.
Zbigniew Jędrzejewski-Szmek 62fe94
Zbigniew Jędrzejewski-Szmek 62fe94
Like idev, the grdev layer is built around session objects. On each
Zbigniew Jędrzejewski-Szmek 62fe94
session object you add/remove graphics devices as they appear and vanish.
Zbigniew Jędrzejewski-Szmek 62fe94
Any device type can be supported via specific card-backends. The exported
Zbigniew Jędrzejewski-Szmek 62fe94
grdev API hides any device details.
Zbigniew Jędrzejewski-Szmek 62fe94
Zbigniew Jędrzejewski-Szmek 62fe94
Graphics devices are represented by "cards". Those are hidden in the
Zbigniew Jędrzejewski-Szmek 62fe94
session and any pipe-configuration is automatically applied. Out of those,
Zbigniew Jędrzejewski-Szmek 62fe94
we configure displays which are then exported to the API user. Displays
Zbigniew Jędrzejewski-Szmek 62fe94
are meant as lowest hardware entity available outside of grdev. The
Zbigniew Jędrzejewski-Szmek 62fe94
underlying pipe configuration is fully hidden and not accessible from the
Zbigniew Jędrzejewski-Szmek 62fe94
outside. The grdev tiling layer allows almost arbitrary setups out of
Zbigniew Jędrzejewski-Szmek 62fe94
multiple pipes, but so far we only use a small subset of this. More will
Zbigniew Jędrzejewski-Szmek 62fe94
follow.
Zbigniew Jędrzejewski-Szmek 62fe94
Zbigniew Jędrzejewski-Szmek 62fe94
A grdev-display is meant to represent real connected displays/monitors.
Zbigniew Jędrzejewski-Szmek 62fe94
The upper level screen arrangements are user policy and not controlled by
Zbigniew Jędrzejewski-Szmek 62fe94
grdev. Applications are free to apply any policy they want.
Zbigniew Jędrzejewski-Szmek 62fe94
Zbigniew Jędrzejewski-Szmek 62fe94
Real card-backends will follow in later patches.
Zbigniew Jędrzejewski-Szmek 62fe94
---
Zbigniew Jędrzejewski-Szmek 62fe94
 Makefile.am                              |    3 +
Zbigniew Jędrzejewski-Szmek 62fe94
 configure.ac                             |    2 +-
Zbigniew Jędrzejewski-Szmek 62fe94
 src/libsystemd-terminal/grdev-internal.h |  237 ++++++
Zbigniew Jędrzejewski-Szmek 62fe94
 src/libsystemd-terminal/grdev.c          | 1219 ++++++++++++++++++++++++++++++
Zbigniew Jędrzejewski-Szmek 62fe94
 src/libsystemd-terminal/grdev.h          |  182 +++++
Zbigniew Jędrzejewski-Szmek 62fe94
 5 files changed, 1642 insertions(+), 1 deletion(-)
Zbigniew Jędrzejewski-Szmek 62fe94
 create mode 100644 src/libsystemd-terminal/grdev-internal.h
Zbigniew Jędrzejewski-Szmek 62fe94
 create mode 100644 src/libsystemd-terminal/grdev.c
Zbigniew Jędrzejewski-Szmek 62fe94
 create mode 100644 src/libsystemd-terminal/grdev.h
Zbigniew Jędrzejewski-Szmek 62fe94
Zbigniew Jędrzejewski-Szmek 62fe94
diff --git a/Makefile.am b/Makefile.am
Zbigniew Jędrzejewski-Szmek 62fe94
index 5dc17f8fe7..1931c5d96b 100644
Zbigniew Jędrzejewski-Szmek 62fe94
--- a/Makefile.am
Zbigniew Jędrzejewski-Szmek 62fe94
+++ b/Makefile.am
Zbigniew Jędrzejewski-Szmek 62fe94
@@ -3005,6 +3005,9 @@ libsystemd_terminal_la_CFLAGS = \
Zbigniew Jędrzejewski-Szmek 62fe94
 	$(TERMINAL_CFLAGS)
Zbigniew Jędrzejewski-Szmek 62fe94
 
Zbigniew Jędrzejewski-Szmek 62fe94
 libsystemd_terminal_la_SOURCES = \
Zbigniew Jędrzejewski-Szmek 62fe94
+	src/libsystemd-terminal/grdev.h \
Zbigniew Jędrzejewski-Szmek 62fe94
+	src/libsystemd-terminal/grdev-internal.h \
Zbigniew Jędrzejewski-Szmek 62fe94
+	src/libsystemd-terminal/grdev.c \
Zbigniew Jędrzejewski-Szmek 62fe94
 	src/libsystemd-terminal/idev.h \
Zbigniew Jędrzejewski-Szmek 62fe94
 	src/libsystemd-terminal/idev-internal.h \
Zbigniew Jędrzejewski-Szmek 62fe94
 	src/libsystemd-terminal/idev.c \
Zbigniew Jędrzejewski-Szmek 62fe94
diff --git a/configure.ac b/configure.ac
Zbigniew Jędrzejewski-Szmek 62fe94
index fb169042d8..38a165c101 100644
Zbigniew Jędrzejewski-Szmek 62fe94
--- a/configure.ac
Zbigniew Jędrzejewski-Szmek 62fe94
+++ b/configure.ac
Zbigniew Jędrzejewski-Szmek 62fe94
@@ -1065,7 +1065,7 @@ AM_CONDITIONAL(ENABLE_MULTI_SEAT_X, [test "$have_multi_seat_x" = "yes"])
Zbigniew Jędrzejewski-Szmek 62fe94
 have_terminal=no
Zbigniew Jędrzejewski-Szmek 62fe94
 AC_ARG_ENABLE(terminal, AS_HELP_STRING([--enable-terminal], [enable terminal support]))
Zbigniew Jędrzejewski-Szmek 62fe94
 if test "x$enable_terminal" = "xyes"; then
Zbigniew Jędrzejewski-Szmek 62fe94
-        PKG_CHECK_MODULES([TERMINAL], [ libevdev >= 1.2 xkbcommon >= 0.4 ], [have_terminal=yes])
Zbigniew Jędrzejewski-Szmek 62fe94
+        PKG_CHECK_MODULES([TERMINAL], [ libevdev >= 1.2 xkbcommon >= 0.4 libdrm >= 2.4], [have_terminal=yes])
Zbigniew Jędrzejewski-Szmek 62fe94
         AS_IF([test "x$have_terminal" != xyes -a "x$enable_terminal" = xyes],
Zbigniew Jędrzejewski-Szmek 62fe94
               [AC_MSG_ERROR([*** terminal support requested but required dependencies not available])],
Zbigniew Jędrzejewski-Szmek 62fe94
               [test "x$have_terminal" = xyes],
Zbigniew Jędrzejewski-Szmek 62fe94
diff --git a/src/libsystemd-terminal/grdev-internal.h b/src/libsystemd-terminal/grdev-internal.h
Zbigniew Jędrzejewski-Szmek 62fe94
new file mode 100644
Zbigniew Jędrzejewski-Szmek 62fe94
index 0000000000..7e69c49b63
Zbigniew Jędrzejewski-Szmek 62fe94
--- /dev/null
Zbigniew Jędrzejewski-Szmek 62fe94
+++ b/src/libsystemd-terminal/grdev-internal.h
Zbigniew Jędrzejewski-Szmek 62fe94
@@ -0,0 +1,237 @@
Zbigniew Jędrzejewski-Szmek 62fe94
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/***
Zbigniew Jędrzejewski-Szmek 62fe94
+  This file is part of systemd.
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+  Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+  systemd is free software; you can redistribute it and/or modify it
Zbigniew Jędrzejewski-Szmek 62fe94
+  under the terms of the GNU Lesser General Public License as published by
Zbigniew Jędrzejewski-Szmek 62fe94
+  the Free Software Foundation; either version 2.1 of the License, or
Zbigniew Jędrzejewski-Szmek 62fe94
+  (at your option) any later version.
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+  systemd is distributed in the hope that it will be useful, but
Zbigniew Jędrzejewski-Szmek 62fe94
+  WITHOUT ANY WARRANTY; without even the implied warranty of
Zbigniew Jędrzejewski-Szmek 62fe94
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Zbigniew Jędrzejewski-Szmek 62fe94
+  Lesser General Public License for more details.
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+  You should have received a copy of the GNU Lesser General Public License
Zbigniew Jędrzejewski-Szmek 62fe94
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
Zbigniew Jędrzejewski-Szmek 62fe94
+***/
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+#pragma once
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <inttypes.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <stdbool.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <stdlib.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <systemd/sd-bus.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <systemd/sd-event.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include "grdev.h"
Zbigniew Jędrzejewski-Szmek 62fe94
+#include "hashmap.h"
Zbigniew Jędrzejewski-Szmek 62fe94
+#include "list.h"
Zbigniew Jędrzejewski-Szmek 62fe94
+#include "util.h"
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+typedef struct grdev_tile               grdev_tile;
Zbigniew Jędrzejewski-Szmek 62fe94
+typedef struct grdev_display_cache      grdev_display_cache;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+typedef struct grdev_pipe_vtable        grdev_pipe_vtable;
Zbigniew Jędrzejewski-Szmek 62fe94
+typedef struct grdev_pipe               grdev_pipe;
Zbigniew Jędrzejewski-Szmek 62fe94
+typedef struct grdev_card_vtable        grdev_card_vtable;
Zbigniew Jędrzejewski-Szmek 62fe94
+typedef struct grdev_card               grdev_card;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Displays
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+enum {
Zbigniew Jędrzejewski-Szmek 62fe94
+        GRDEV_TILE_LEAF,
Zbigniew Jędrzejewski-Szmek 62fe94
+        GRDEV_TILE_NODE,
Zbigniew Jędrzejewski-Szmek 62fe94
+        GRDEV_TILE_CNT
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+struct grdev_tile {
Zbigniew Jędrzejewski-Szmek 62fe94
+        LIST_FIELDS(grdev_tile, childs_by_node);
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_tile *parent;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_display *display;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t x;
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t y;
Zbigniew Jędrzejewski-Szmek 62fe94
+        unsigned int rotate;
Zbigniew Jędrzejewski-Szmek 62fe94
+        unsigned int flip;
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t cache_w;
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t cache_h;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        unsigned int type;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        union {
Zbigniew Jędrzejewski-Szmek 62fe94
+                struct {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        grdev_pipe *pipe;
Zbigniew Jędrzejewski-Szmek 62fe94
+                } leaf;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                struct {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        size_t n_childs;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        LIST_HEAD(grdev_tile, child_list);
Zbigniew Jędrzejewski-Szmek 62fe94
+                } node;
Zbigniew Jędrzejewski-Szmek 62fe94
+        };
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+int grdev_tile_new_leaf(grdev_tile **out, grdev_pipe *pipe);
Zbigniew Jędrzejewski-Szmek 62fe94
+int grdev_tile_new_node(grdev_tile **out);
Zbigniew Jędrzejewski-Szmek 62fe94
+grdev_tile *grdev_tile_free(grdev_tile *tile);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+DEFINE_TRIVIAL_CLEANUP_FUNC(grdev_tile*, grdev_tile_free);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+struct grdev_display {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_session *session;
Zbigniew Jędrzejewski-Szmek 62fe94
+        char *name;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        size_t n_leafs;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_tile *tile;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        size_t n_pipes;
Zbigniew Jędrzejewski-Szmek 62fe94
+        size_t max_pipes;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t width;
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t height;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        struct grdev_display_cache {
Zbigniew Jędrzejewski-Szmek 62fe94
+                grdev_pipe *pipe;
Zbigniew Jędrzejewski-Szmek 62fe94
+                grdev_display_target target;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                bool incomplete : 1;
Zbigniew Jędrzejewski-Szmek 62fe94
+        } *pipes;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        bool enabled : 1;
Zbigniew Jędrzejewski-Szmek 62fe94
+        bool public : 1;
Zbigniew Jędrzejewski-Szmek 62fe94
+        bool modified : 1;
Zbigniew Jędrzejewski-Szmek 62fe94
+        bool framed : 1;
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+grdev_display *grdev_find_display(grdev_session *session, const char *name);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+int grdev_display_new(grdev_display **out, grdev_session *session, const char *name);
Zbigniew Jędrzejewski-Szmek 62fe94
+grdev_display *grdev_display_free(grdev_display *display);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+DEFINE_TRIVIAL_CLEANUP_FUNC(grdev_display*, grdev_display_free);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Pipes
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+struct grdev_pipe_vtable {
Zbigniew Jędrzejewski-Szmek 62fe94
+        void (*free) (grdev_pipe *pipe);
Zbigniew Jędrzejewski-Szmek 62fe94
+        void (*enable) (grdev_pipe *pipe);
Zbigniew Jędrzejewski-Szmek 62fe94
+        void (*disable) (grdev_pipe *pipe);
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_fb *(*target) (grdev_pipe *pipe);
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+struct grdev_pipe {
Zbigniew Jędrzejewski-Szmek 62fe94
+        const grdev_pipe_vtable *vtable;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_card *card;
Zbigniew Jędrzejewski-Szmek 62fe94
+        char *name;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_tile *tile;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_display_cache *cache;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t width;
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t height;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        size_t max_fbs;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_fb *front;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_fb *back;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_fb **fbs;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        bool enabled : 1;
Zbigniew Jędrzejewski-Szmek 62fe94
+        bool running : 1;
Zbigniew Jędrzejewski-Szmek 62fe94
+        bool flip : 1;
Zbigniew Jędrzejewski-Szmek 62fe94
+        bool flipping : 1;
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+#define GRDEV_PIPE_INIT(_vtable, _card) ((grdev_pipe){ \
Zbigniew Jędrzejewski-Szmek 62fe94
+                .vtable = (_vtable), \
Zbigniew Jędrzejewski-Szmek 62fe94
+                .card = (_card), \
Zbigniew Jędrzejewski-Szmek 62fe94
+        })
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+grdev_pipe *grdev_find_pipe(grdev_card *card, const char *name);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+int grdev_pipe_add(grdev_pipe *pipe, const char *name, size_t n_fbs);
Zbigniew Jędrzejewski-Szmek 62fe94
+grdev_pipe *grdev_pipe_free(grdev_pipe *pipe);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+DEFINE_TRIVIAL_CLEANUP_FUNC(grdev_pipe*, grdev_pipe_free);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+void grdev_pipe_ready(grdev_pipe *pipe, bool running);
Zbigniew Jędrzejewski-Szmek 62fe94
+void grdev_pipe_frame(grdev_pipe *pipe);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Cards
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+struct grdev_card_vtable {
Zbigniew Jędrzejewski-Szmek 62fe94
+        void (*free) (grdev_card *card);
Zbigniew Jędrzejewski-Szmek 62fe94
+        void (*enable) (grdev_card *card);
Zbigniew Jędrzejewski-Szmek 62fe94
+        void (*disable) (grdev_card *card);
Zbigniew Jędrzejewski-Szmek 62fe94
+        void (*commit) (grdev_card *card);
Zbigniew Jędrzejewski-Szmek 62fe94
+        void (*restore) (grdev_card *card);
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+struct grdev_card {
Zbigniew Jędrzejewski-Szmek 62fe94
+        const grdev_card_vtable *vtable;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_session *session;
Zbigniew Jędrzejewski-Szmek 62fe94
+        char *name;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        Hashmap *pipe_map;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        bool enabled : 1;
Zbigniew Jędrzejewski-Szmek 62fe94
+        bool modified : 1;
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+#define GRDEV_CARD_INIT(_vtable, _session) ((grdev_card){ \
Zbigniew Jędrzejewski-Szmek 62fe94
+                .vtable = (_vtable), \
Zbigniew Jędrzejewski-Szmek 62fe94
+                .session = (_session), \
Zbigniew Jędrzejewski-Szmek 62fe94
+        })
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+grdev_card *grdev_find_card(grdev_session *session, const char *name);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+int grdev_card_add(grdev_card *card, const char *name);
Zbigniew Jędrzejewski-Szmek 62fe94
+grdev_card *grdev_card_free(grdev_card *card);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+DEFINE_TRIVIAL_CLEANUP_FUNC(grdev_card*, grdev_card_free);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Sessions
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+struct grdev_session {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_context *context;
Zbigniew Jędrzejewski-Szmek 62fe94
+        char *name;
Zbigniew Jędrzejewski-Szmek 62fe94
+        char *path;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_event_fn event_fn;
Zbigniew Jędrzejewski-Szmek 62fe94
+        void *userdata;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        unsigned long n_pins;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        Hashmap *card_map;
Zbigniew Jędrzejewski-Szmek 62fe94
+        Hashmap *display_map;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        bool custom : 1;
Zbigniew Jędrzejewski-Szmek 62fe94
+        bool managed : 1;
Zbigniew Jędrzejewski-Szmek 62fe94
+        bool enabled : 1;
Zbigniew Jędrzejewski-Szmek 62fe94
+        bool modified : 1;
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+grdev_session *grdev_session_pin(grdev_session *session);
Zbigniew Jędrzejewski-Szmek 62fe94
+grdev_session *grdev_session_unpin(grdev_session *session);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+DEFINE_TRIVIAL_CLEANUP_FUNC(grdev_session*, grdev_session_unpin);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Contexts
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+struct grdev_context {
Zbigniew Jędrzejewski-Szmek 62fe94
+        unsigned long ref;
Zbigniew Jędrzejewski-Szmek 62fe94
+        sd_event *event;
Zbigniew Jędrzejewski-Szmek 62fe94
+        sd_bus *sysbus;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        Hashmap *session_map;
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
diff --git a/src/libsystemd-terminal/grdev.c b/src/libsystemd-terminal/grdev.c
Zbigniew Jędrzejewski-Szmek 62fe94
new file mode 100644
Zbigniew Jędrzejewski-Szmek 62fe94
index 0000000000..ab1c407ecb
Zbigniew Jędrzejewski-Szmek 62fe94
--- /dev/null
Zbigniew Jędrzejewski-Szmek 62fe94
+++ b/src/libsystemd-terminal/grdev.c
Zbigniew Jędrzejewski-Szmek 62fe94
@@ -0,0 +1,1219 @@
Zbigniew Jędrzejewski-Szmek 62fe94
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/***
Zbigniew Jędrzejewski-Szmek 62fe94
+  This file is part of systemd.
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+  Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+  systemd is free software; you can redistribute it and/or modify it
Zbigniew Jędrzejewski-Szmek 62fe94
+  under the terms of the GNU Lesser General Public License as published by
Zbigniew Jędrzejewski-Szmek 62fe94
+  the Free Software Foundation; either version 2.1 of the License, or
Zbigniew Jędrzejewski-Szmek 62fe94
+  (at your option) any later version.
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+  systemd is distributed in the hope that it will be useful, but
Zbigniew Jędrzejewski-Szmek 62fe94
+  WITHOUT ANY WARRANTY; without even the implied warranty of
Zbigniew Jędrzejewski-Szmek 62fe94
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Zbigniew Jędrzejewski-Szmek 62fe94
+  Lesser General Public License for more details.
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+  You should have received a copy of the GNU Lesser General Public License
Zbigniew Jędrzejewski-Szmek 62fe94
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
Zbigniew Jędrzejewski-Szmek 62fe94
+***/
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <inttypes.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <stdbool.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <stdlib.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <systemd/sd-bus.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <systemd/sd-event.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <systemd/sd-login.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include "grdev.h"
Zbigniew Jędrzejewski-Szmek 62fe94
+#include "grdev-internal.h"
Zbigniew Jędrzejewski-Szmek 62fe94
+#include "hashmap.h"
Zbigniew Jędrzejewski-Szmek 62fe94
+#include "login-shared.h"
Zbigniew Jędrzejewski-Szmek 62fe94
+#include "macro.h"
Zbigniew Jędrzejewski-Szmek 62fe94
+#include "util.h"
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void pipe_enable(grdev_pipe *pipe);
Zbigniew Jędrzejewski-Szmek 62fe94
+static void pipe_disable(grdev_pipe *pipe);
Zbigniew Jędrzejewski-Szmek 62fe94
+static void card_modified(grdev_card *card);
Zbigniew Jędrzejewski-Szmek 62fe94
+static void session_frame(grdev_session *session, grdev_display *display);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Displays
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static inline grdev_tile *tile_leftmost(grdev_tile *tile) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!tile)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        while (tile->type == GRDEV_TILE_NODE && tile->node.child_list)
Zbigniew Jędrzejewski-Szmek 62fe94
+                tile = tile->node.child_list;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return tile;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+#define TILE_FOREACH(_root, _i) \
Zbigniew Jędrzejewski-Szmek 62fe94
+        for (_i = tile_leftmost(_root); _i; _i = tile_leftmost(_i->childs_by_node_next) ? : _i->parent)
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+#define TILE_FOREACH_SAFE(_root, _i, _next) \
Zbigniew Jędrzejewski-Szmek 62fe94
+        for (_i = tile_leftmost(_root); _i && ((_next = tile_leftmost(_i->childs_by_node_next) ? : _i->parent), true); _i = _next)
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void tile_link(grdev_tile *tile, grdev_tile *parent) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_display *display;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_tile *t;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(tile);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(!tile->parent);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(!tile->display);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(parent);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(parent->type == GRDEV_TILE_NODE);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        display = parent->display;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(!display || !display->enabled);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        ++parent->node.n_childs;
Zbigniew Jędrzejewski-Szmek 62fe94
+        LIST_PREPEND(childs_by_node, parent->node.child_list, tile);
Zbigniew Jędrzejewski-Szmek 62fe94
+        tile->parent = parent;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (display) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                display->modified = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+                TILE_FOREACH(tile, t) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        t->display = display;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (t->type == GRDEV_TILE_LEAF) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                ++display->n_leafs;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                if (display->enabled)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                        pipe_enable(t->leaf.pipe);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        }
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void tile_unlink(grdev_tile *tile) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_tile *parent, *t;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_display *display;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(tile);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        display = tile->display;
Zbigniew Jędrzejewski-Szmek 62fe94
+        parent = tile->parent;
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!parent) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                assert(!display);
Zbigniew Jędrzejewski-Szmek 62fe94
+                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(parent->type == GRDEV_TILE_NODE);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(parent->display == display);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(parent->node.n_childs > 0);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        --parent->node.n_childs;
Zbigniew Jędrzejewski-Szmek 62fe94
+        LIST_REMOVE(childs_by_node, parent->node.child_list, tile);
Zbigniew Jędrzejewski-Szmek 62fe94
+        tile->parent = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (display) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                display->modified = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+                TILE_FOREACH(tile, t) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        t->display = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (t->type == GRDEV_TILE_LEAF) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                --display->n_leafs;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                t->leaf.pipe->cache = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                pipe_disable(t->leaf.pipe);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        }
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* Tile trees are driven by leafs. Internal nodes have no owner, thus,
Zbigniew Jędrzejewski-Szmek 62fe94
+         * we must take care to not leave them around. Therefore, whenever we
Zbigniew Jędrzejewski-Szmek 62fe94
+         * unlink any part of a tree, we also destroy the parent, in case it's
Zbigniew Jędrzejewski-Szmek 62fe94
+         * now stale.
Zbigniew Jędrzejewski-Szmek 62fe94
+         * Parents are stale if they have no childs and either have no display
Zbigniew Jędrzejewski-Szmek 62fe94
+         * or if they are intermediate nodes (i.e, they have a parent).
Zbigniew Jędrzejewski-Szmek 62fe94
+         * This means, you can easily create trees, but you can never partially
Zbigniew Jędrzejewski-Szmek 62fe94
+         * move or destruct them so far. They're always reduced to minimal form
Zbigniew Jędrzejewski-Szmek 62fe94
+         * if you cut them. This might change later, but so far we didn't need
Zbigniew Jędrzejewski-Szmek 62fe94
+         * partial destruction or the ability to move whole trees. */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (parent->node.n_childs < 1 && (parent->parent || !parent->display))
Zbigniew Jędrzejewski-Szmek 62fe94
+                grdev_tile_free(parent);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int tile_new(grdev_tile **out) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        _cleanup_(grdev_tile_freep) grdev_tile *tile = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(out);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        tile = new0(grdev_tile, 1);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!tile)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        tile->type = (unsigned)-1;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        *out = tile;
Zbigniew Jędrzejewski-Szmek 62fe94
+        tile = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+int grdev_tile_new_leaf(grdev_tile **out, grdev_pipe *pipe) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        _cleanup_(grdev_tile_freep) grdev_tile *tile = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(pipe, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(!pipe->tile, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = tile_new(&tile;;
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        tile->type = GRDEV_TILE_LEAF;
Zbigniew Jędrzejewski-Szmek 62fe94
+        tile->leaf.pipe = pipe;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (out)
Zbigniew Jędrzejewski-Szmek 62fe94
+                *out = tile;
Zbigniew Jędrzejewski-Szmek 62fe94
+        tile = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+int grdev_tile_new_node(grdev_tile **out) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        _cleanup_(grdev_tile_freep) grdev_tile *tile = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(out, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = tile_new(&tile;;
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        tile->type = GRDEV_TILE_NODE;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        *out = tile;
Zbigniew Jędrzejewski-Szmek 62fe94
+        tile = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+grdev_tile *grdev_tile_free(grdev_tile *tile) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!tile)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        tile_unlink(tile);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        switch (tile->type) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        case GRDEV_TILE_LEAF:
Zbigniew Jędrzejewski-Szmek 62fe94
+                assert(!tile->parent);
Zbigniew Jędrzejewski-Szmek 62fe94
+                assert(!tile->display);
Zbigniew Jędrzejewski-Szmek 62fe94
+                assert(tile->leaf.pipe);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                break;
Zbigniew Jędrzejewski-Szmek 62fe94
+        case GRDEV_TILE_NODE:
Zbigniew Jędrzejewski-Szmek 62fe94
+                assert(!tile->parent);
Zbigniew Jędrzejewski-Szmek 62fe94
+                assert(!tile->display);
Zbigniew Jędrzejewski-Szmek 62fe94
+                assert(tile->node.n_childs == 0);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                break;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(tile);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+grdev_display *grdev_find_display(grdev_session *session, const char *name) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(session, NULL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(name, NULL);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return hashmap_get(session->display_map, name);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+int grdev_display_new(grdev_display **out, grdev_session *session, const char *name) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        _cleanup_(grdev_display_freep) grdev_display *display = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(session);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(name);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        display = new0(grdev_display, 1);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!display)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        display->session = session;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        display->name = strdup(name);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!display->name)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = grdev_tile_new_node(&display->tile);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        display->tile->display = display;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = hashmap_put(session->display_map, display->name, display);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (out)
Zbigniew Jędrzejewski-Szmek 62fe94
+                *out = display;
Zbigniew Jędrzejewski-Szmek 62fe94
+        display = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+grdev_display *grdev_display_free(grdev_display *display) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!display)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(!display->public);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(!display->enabled);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(!display->modified);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(display->n_leafs == 0);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(display->n_pipes == 0);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (display->name)
Zbigniew Jędrzejewski-Szmek 62fe94
+                hashmap_remove_value(display->session->display_map, display->name, display);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (display->tile) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                display->tile->display = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+                grdev_tile_free(display->tile);
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(display->pipes);
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(display->name);
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(display);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+bool grdev_display_is_enabled(grdev_display *display) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        return display && display->enabled;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+void grdev_display_enable(grdev_display *display) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_tile *t;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(display);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!display->enabled) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                display->enabled = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+                TILE_FOREACH(display->tile, t)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (t->type == GRDEV_TILE_LEAF)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                pipe_enable(t->leaf.pipe);
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+void grdev_display_disable(grdev_display *display) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_tile *t;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(display);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (display->enabled) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                display->enabled = false;
Zbigniew Jędrzejewski-Szmek 62fe94
+                TILE_FOREACH(display->tile, t)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (t->type == GRDEV_TILE_LEAF)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                pipe_disable(t->leaf.pipe);
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+const grdev_display_target *grdev_display_next_target(grdev_display *display, const grdev_display_target *prev, uint64_t minage) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_display_cache *cache;
Zbigniew Jędrzejewski-Szmek 62fe94
+        size_t idx;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(display, NULL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(!display->modified, NULL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(display->enabled, NULL);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (prev) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                cache = container_of(prev, grdev_display_cache, target);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                assert(cache->pipe);
Zbigniew Jędrzejewski-Szmek 62fe94
+                assert(cache->pipe->tile->display == display);
Zbigniew Jędrzejewski-Szmek 62fe94
+                assert(display->pipes >= cache);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                idx = (cache - display->pipes) / sizeof(*cache) + 1;
Zbigniew Jędrzejewski-Szmek 62fe94
+        } else {
Zbigniew Jędrzejewski-Szmek 62fe94
+                idx = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        for (cache = display->pipes + idx; idx < display->n_pipes; ++idx, ++cache) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                grdev_display_target *target;
Zbigniew Jędrzejewski-Szmek 62fe94
+                grdev_pipe *pipe;
Zbigniew Jędrzejewski-Szmek 62fe94
+                grdev_fb *fb;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                pipe = cache->pipe;
Zbigniew Jędrzejewski-Szmek 62fe94
+                target = &cache->target;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (!pipe->running || !pipe->enabled)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                /* if front-buffer is up-to-date, there's nothing to do */
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (minage > 0 && pipe->front && pipe->front->age >= minage)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                /* find suitable back-buffer */
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (!(fb = pipe->back)) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (!pipe->vtable->target || !(fb = pipe->vtable->target(pipe)))
Zbigniew Jędrzejewski-Szmek 62fe94
+                                continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                /* if back-buffer is up-to-date, schedule flip */
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (minage > 0 && fb->age >= minage) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        grdev_display_flip_target(display, target, fb->age);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                /* we have an out-of-date back-buffer; return for redraw */
Zbigniew Jędrzejewski-Szmek 62fe94
+                target->fb = fb;
Zbigniew Jędrzejewski-Szmek 62fe94
+                return target;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+void grdev_display_flip_target(grdev_display *display, const grdev_display_target *target, uint64_t age) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_display_cache *cache;
Zbigniew Jędrzejewski-Szmek 62fe94
+        size_t i;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(display);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(!display->modified);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(display->enabled);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(target);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(target->fb);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        cache = container_of(target, grdev_display_cache, target);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(cache->pipe);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(cache->pipe->tile->display == display);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* reset age of all FB on overflow */
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (age < target->fb->age)
Zbigniew Jędrzejewski-Szmek 62fe94
+                for (i = 0; i < cache->pipe->max_fbs; ++i)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (cache->pipe->fbs[i])
Zbigniew Jędrzejewski-Szmek 62fe94
+                                cache->pipe->fbs[i]->age = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        ((grdev_fb*)target->fb)->age = age;
Zbigniew Jędrzejewski-Szmek 62fe94
+        cache->pipe->flip = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void display_cache_apply(grdev_display_cache *c, grdev_tile *l) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t x, y, width, height;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_display_target *t;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(c);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(l);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(l->cache_w >= c->target.width + c->target.x);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(l->cache_h >= c->target.height + c->target.y);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        t = &c->target;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* rotate child */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        t->rotate = (t->rotate + l->rotate) & 0x3;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        x = t->x;
Zbigniew Jędrzejewski-Szmek 62fe94
+        y = t->y;
Zbigniew Jędrzejewski-Szmek 62fe94
+        width = t->width;
Zbigniew Jędrzejewski-Szmek 62fe94
+        height = t->height;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        switch (l->rotate) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        case GRDEV_ROTATE_0:
Zbigniew Jędrzejewski-Szmek 62fe94
+                break;
Zbigniew Jędrzejewski-Szmek 62fe94
+        case GRDEV_ROTATE_90:
Zbigniew Jędrzejewski-Szmek 62fe94
+                t->x = l->cache_h - (height + y);
Zbigniew Jędrzejewski-Szmek 62fe94
+                t->y = x;
Zbigniew Jędrzejewski-Szmek 62fe94
+                t->width = height;
Zbigniew Jędrzejewski-Szmek 62fe94
+                t->height = width;
Zbigniew Jędrzejewski-Szmek 62fe94
+                break;
Zbigniew Jędrzejewski-Szmek 62fe94
+        case GRDEV_ROTATE_180:
Zbigniew Jędrzejewski-Szmek 62fe94
+                t->x = l->cache_w - (width + x);
Zbigniew Jędrzejewski-Szmek 62fe94
+                t->y = l->cache_h - (height + y);
Zbigniew Jędrzejewski-Szmek 62fe94
+                break;
Zbigniew Jędrzejewski-Szmek 62fe94
+        case GRDEV_ROTATE_270:
Zbigniew Jędrzejewski-Szmek 62fe94
+                t->x = y;
Zbigniew Jędrzejewski-Szmek 62fe94
+                t->y = l->cache_w - (width + x);
Zbigniew Jędrzejewski-Szmek 62fe94
+                t->width = height;
Zbigniew Jędrzejewski-Szmek 62fe94
+                t->height = width;
Zbigniew Jędrzejewski-Szmek 62fe94
+                break;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* flip child */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        t->flip ^= l->flip;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (l->flip & GRDEV_FLIP_HORIZONTAL)
Zbigniew Jędrzejewski-Szmek 62fe94
+                t->x = l->cache_w - (t->width + t->x);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (l->flip & GRDEV_FLIP_VERTICAL)
Zbigniew Jędrzejewski-Szmek 62fe94
+                t->y = l->cache_h - (t->height + t->y);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* move child */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        t->x += l->x;
Zbigniew Jędrzejewski-Szmek 62fe94
+        t->y += l->y;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void display_cache_targets(grdev_display *display) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_display_cache *c;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_tile *tile;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(display);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* depth-first with childs before parent */
Zbigniew Jędrzejewski-Szmek 62fe94
+        for (tile = tile_leftmost(display->tile);
Zbigniew Jędrzejewski-Szmek 62fe94
+             tile;
Zbigniew Jędrzejewski-Szmek 62fe94
+             tile = tile_leftmost(tile->childs_by_node_next) ? : tile->parent) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (tile->type == GRDEV_TILE_LEAF) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        grdev_pipe *p;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        /* We're at a leaf and no parent has been cached, yet.
Zbigniew Jędrzejewski-Szmek 62fe94
+                         * Copy the pipe information into the target cache and
Zbigniew Jędrzejewski-Szmek 62fe94
+                         * update our global pipe-caches if required. */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        assert(tile->leaf.pipe);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        assert(display->n_pipes + 1 <= display->max_pipes);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        p = tile->leaf.pipe;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        c = &display->pipes[display->n_pipes++];
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        zero(*c);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        c->pipe = p;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        c->pipe->cache = c;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        c->target.width = p->width;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        c->target.height = p->height;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        tile->cache_w = p->width;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        tile->cache_h = p->height;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        /* all new tiles are incomplete due to geometry changes */
Zbigniew Jędrzejewski-Szmek 62fe94
+                        c->incomplete = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        display_cache_apply(c, tile);
Zbigniew Jędrzejewski-Szmek 62fe94
+                } else {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        grdev_tile *child, *l;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        /* We're now at a node with all it's childs already
Zbigniew Jędrzejewski-Szmek 62fe94
+                         * computed (depth-first, child before parent). We
Zbigniew Jędrzejewski-Szmek 62fe94
+                         * first need to know the size of our tile, then we
Zbigniew Jędrzejewski-Szmek 62fe94
+                         * recurse into all leafs and update their cache. */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        tile->cache_w = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        tile->cache_h = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        LIST_FOREACH(childs_by_node, child, tile->node.child_list) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                if (child->x + child->cache_w > tile->cache_w)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                        tile->cache_w = child->x + child->cache_w;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                if (child->y + child->cache_h > tile->cache_h)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                        tile->cache_h = child->y + child->cache_h;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        assert(tile->cache_w > 0);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        assert(tile->cache_h > 0);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        TILE_FOREACH(tile, l)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                if (l->type == GRDEV_TILE_LEAF)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                        display_cache_apply(l->leaf.pipe->cache, tile);
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static bool display_cache(grdev_display *display) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_tile *tile;
Zbigniew Jędrzejewski-Szmek 62fe94
+        size_t n;
Zbigniew Jędrzejewski-Szmek 62fe94
+        void *t;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(display);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!display->modified)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return false;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        display->modified = false;
Zbigniew Jędrzejewski-Szmek 62fe94
+        display->framed = false;
Zbigniew Jędrzejewski-Szmek 62fe94
+        display->n_pipes = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+        display->width = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+        display->height = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (display->n_leafs < 1)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return false;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        TILE_FOREACH(display->tile, tile)
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (tile->type == GRDEV_TILE_LEAF)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        tile->leaf.pipe->cache = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (display->n_leafs > display->max_pipes) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                n = ALIGN_POWER2(display->n_leafs);
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (!n) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        r = -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        goto out;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                t = realloc_multiply(display->pipes, sizeof(*display->pipes), n);
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (!t) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        r = -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        goto out;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                display->pipes = t;
Zbigniew Jędrzejewski-Szmek 62fe94
+                display->max_pipes = n;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        display_cache_targets(display);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+out:
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("grdev: %s/%s: cannot cache pipes: %s",
Zbigniew Jędrzejewski-Szmek 62fe94
+                          display->session->name, display->name, strerror(-r));
Zbigniew Jędrzejewski-Szmek 62fe94
+        return true;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Pipes
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+grdev_pipe *grdev_find_pipe(grdev_card *card, const char *name) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(card, NULL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(name, NULL);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return hashmap_get(card->pipe_map, name);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+int grdev_pipe_add(grdev_pipe *pipe, const char *name, size_t n_fbs) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(pipe, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(pipe->vtable, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(pipe->vtable->free, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(pipe->card, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(pipe->card->session, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(!pipe->cache, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(pipe->width > 0, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(pipe->height > 0, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(!pipe->enabled, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(!pipe->running, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(name, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        pipe->name = strdup(name);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!pipe->name)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (n_fbs > 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                pipe->fbs = new0(grdev_fb*, n_fbs);
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (!pipe->fbs)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                pipe->max_fbs = n_fbs;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = grdev_tile_new_leaf(&pipe->tile, pipe);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = hashmap_put(pipe->card->pipe_map, pipe->name, pipe);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        card_modified(pipe->card);
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+grdev_pipe *grdev_pipe_free(grdev_pipe *pipe) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_pipe tmp;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!pipe)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(pipe->card);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(pipe->vtable);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(pipe->vtable->free);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (pipe->name)
Zbigniew Jędrzejewski-Szmek 62fe94
+                hashmap_remove_value(pipe->card->pipe_map, pipe->name, pipe);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (pipe->tile)
Zbigniew Jędrzejewski-Szmek 62fe94
+                tile_unlink(pipe->tile);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(!pipe->cache);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        tmp = *pipe;
Zbigniew Jędrzejewski-Szmek 62fe94
+        pipe->vtable->free(pipe);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_tile_free(tmp.tile);
Zbigniew Jędrzejewski-Szmek 62fe94
+        card_modified(tmp.card);
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(tmp.fbs);
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(tmp.name);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void pipe_enable(grdev_pipe *pipe) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(pipe);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!pipe->enabled) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                pipe->enabled = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (pipe->vtable->enable)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        pipe->vtable->enable(pipe);
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void pipe_disable(grdev_pipe *pipe) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(pipe);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (pipe->enabled) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                pipe->enabled = false;
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (pipe->vtable->disable)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        pipe->vtable->disable(pipe);
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+void grdev_pipe_ready(grdev_pipe *pipe, bool running) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(pipe);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* grdev_pipe_ready() is used by backends to notify about pipe state
Zbigniew Jędrzejewski-Szmek 62fe94
+         * changed. If a pipe is ready, it can be fully used by us (available,
Zbigniew Jędrzejewski-Szmek 62fe94
+         * enabled and accessable). Backends can disable pipes at any time
Zbigniew Jędrzejewski-Szmek 62fe94
+         * (like for async revocation), but can only enable them from parent
Zbigniew Jędrzejewski-Szmek 62fe94
+         * context. Otherwise, we might call user-callbacks recursively. */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (pipe->running == running)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        pipe->running = running;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* runtime events for unused pipes are not interesting */
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (pipe->cache) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                grdev_display *display = pipe->tile->display;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                assert(display);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (running) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (pipe->enabled)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                session_frame(display->session, display);
Zbigniew Jędrzejewski-Szmek 62fe94
+                } else {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        pipe->cache->incomplete = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+void grdev_pipe_frame(grdev_pipe *pipe) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_display *display;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(pipe);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* if pipe is unused, ignore any frame events */
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!pipe->cache)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        display = pipe->tile->display;
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(display);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (pipe->enabled)
Zbigniew Jędrzejewski-Szmek 62fe94
+                session_frame(display->session, display);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Cards
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+grdev_card *grdev_find_card(grdev_session *session, const char *name) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(session, NULL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(name, NULL);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return hashmap_get(session->card_map, name);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+int grdev_card_add(grdev_card *card, const char *name) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(card, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(card->vtable, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(card->vtable->free, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(card->session, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(name, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        card->name = strdup(name);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!card->name)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        card->pipe_map = hashmap_new(&string_hash_ops);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!card->pipe_map)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = hashmap_put(card->session->card_map, card->name, card);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+grdev_card *grdev_card_free(grdev_card *card) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_card tmp;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!card)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(!card->enabled);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(card->vtable);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(card->vtable->free);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (card->name)
Zbigniew Jędrzejewski-Szmek 62fe94
+                hashmap_remove_value(card->session->card_map, card->name, card);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        tmp = *card;
Zbigniew Jędrzejewski-Szmek 62fe94
+        card->vtable->free(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(hashmap_size(tmp.pipe_map) == 0);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        hashmap_free(tmp.pipe_map);
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(tmp.name);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void card_modified(grdev_card *card) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(card->session->n_pins > 0);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        card->modified = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void grdev_card_enable(grdev_card *card) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!card->enabled) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                card->enabled = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (card->vtable->enable)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        card->vtable->enable(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void grdev_card_disable(grdev_card *card) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (card->enabled) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                card->enabled = false;
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (card->vtable->disable)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        card->vtable->disable(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Sessions
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void session_raise(grdev_session *session, grdev_event *event) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        session->event_fn(session, session->userdata, event);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void session_raise_display_add(grdev_session *session, grdev_display *display) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_event event = {
Zbigniew Jędrzejewski-Szmek 62fe94
+                .type = GRDEV_EVENT_DISPLAY_ADD,
Zbigniew Jędrzejewski-Szmek 62fe94
+                .display_add = {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        .display = display,
Zbigniew Jędrzejewski-Szmek 62fe94
+                },
Zbigniew Jędrzejewski-Szmek 62fe94
+        };
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        session_raise(session, &event);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void session_raise_display_remove(grdev_session *session, grdev_display *display) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_event event = {
Zbigniew Jędrzejewski-Szmek 62fe94
+                .type = GRDEV_EVENT_DISPLAY_REMOVE,
Zbigniew Jędrzejewski-Szmek 62fe94
+                .display_remove = {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        .display = display,
Zbigniew Jędrzejewski-Szmek 62fe94
+                },
Zbigniew Jędrzejewski-Szmek 62fe94
+        };
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        session_raise(session, &event);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void session_raise_display_change(grdev_session *session, grdev_display *display) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_event event = {
Zbigniew Jędrzejewski-Szmek 62fe94
+                .type = GRDEV_EVENT_DISPLAY_CHANGE,
Zbigniew Jędrzejewski-Szmek 62fe94
+                .display_change = {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        .display = display,
Zbigniew Jędrzejewski-Szmek 62fe94
+                },
Zbigniew Jędrzejewski-Szmek 62fe94
+        };
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        session_raise(session, &event);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void session_raise_display_frame(grdev_session *session, grdev_display *display) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_event event = {
Zbigniew Jędrzejewski-Szmek 62fe94
+                .type = GRDEV_EVENT_DISPLAY_FRAME,
Zbigniew Jędrzejewski-Szmek 62fe94
+                .display_frame = {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        .display = display,
Zbigniew Jędrzejewski-Szmek 62fe94
+                },
Zbigniew Jędrzejewski-Szmek 62fe94
+        };
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        session_raise(session, &event);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void session_add_card(grdev_session *session, grdev_card *card) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(session);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        log_debug("grdev: %s: add card '%s'", session->name, card->name);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* Cards are not exposed to users, but managed internally. Cards are
Zbigniew Jędrzejewski-Szmek 62fe94
+         * enabled if the session is enabled, and will track that state. The
Zbigniew Jędrzejewski-Szmek 62fe94
+         * backend can probe the card at any time, but only if enabled. It
Zbigniew Jędrzejewski-Szmek 62fe94
+         * will then add pipes according to hardware state.
Zbigniew Jędrzejewski-Szmek 62fe94
+         * That is, the card may create pipes as soon as we enable it here. */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (session->enabled)
Zbigniew Jędrzejewski-Szmek 62fe94
+                grdev_card_enable(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void session_remove_card(grdev_session *session, grdev_card *card) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(session);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        log_debug("grdev: %s: remove card '%s'", session->name, card->name);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* As cards are not exposed, it can never be accessed by outside
Zbigniew Jędrzejewski-Szmek 62fe94
+         * users and we can simply remove it. Disabling the card does not
Zbigniew Jędrzejewski-Szmek 62fe94
+         * necessarily drop all pipes of the card. This is usually deferred
Zbigniew Jędrzejewski-Szmek 62fe94
+         * to card destruction (as pipes are cached as long as FDs remain
Zbigniew Jędrzejewski-Szmek 62fe94
+         * open). Therefore, the card destruction might cause pipes, and thus
Zbigniew Jędrzejewski-Szmek 62fe94
+         * visible displays, to be removed. */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_card_disable(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_card_free(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void session_add_display(grdev_session *session, grdev_display *display) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(session);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(display);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(!display->enabled);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        log_debug("grdev: %s: add display '%s'", session->name, display->name);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* Displays are the main entity for public API users. We create them
Zbigniew Jędrzejewski-Szmek 62fe94
+         * independent of card backends and they wrap any underlying display
Zbigniew Jędrzejewski-Szmek 62fe94
+         * architecture. Displays are public at all times, thus, may be entered
Zbigniew Jędrzejewski-Szmek 62fe94
+         * by outside users at any time. */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        display->public = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+        session_raise_display_add(session, display);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void session_remove_display(grdev_session *session, grdev_display *display) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(session);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(display);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        log_debug("grdev: %s: remove display '%s'", session->name, display->name);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* Displays are public, so we have to be careful when removing them.
Zbigniew Jędrzejewski-Szmek 62fe94
+         * We first tell users about their removal, disable them and then drop
Zbigniew Jędrzejewski-Szmek 62fe94
+         * them. We now, after the notification, no external access will
Zbigniew Jędrzejewski-Szmek 62fe94
+         * happen. Therefore, we can release the tiles afterwards safely. */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (display->public) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                display->public = false;
Zbigniew Jędrzejewski-Szmek 62fe94
+                session_raise_display_remove(session, display);
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_display_disable(display);
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_display_free(display);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void session_change_display(grdev_session *session, grdev_display *display) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        bool changed;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(session);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(display);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        changed = display_cache(display);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (display->n_leafs == 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                session_remove_display(session, display);
Zbigniew Jędrzejewski-Szmek 62fe94
+        else if (!display->public)
Zbigniew Jędrzejewski-Szmek 62fe94
+                session_add_display(session, display);
Zbigniew Jędrzejewski-Szmek 62fe94
+        else if (changed)
Zbigniew Jędrzejewski-Szmek 62fe94
+                session_raise_display_change(session, display);
Zbigniew Jędrzejewski-Szmek 62fe94
+        else if (display->framed)
Zbigniew Jędrzejewski-Szmek 62fe94
+                session_frame(session, display);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void session_frame(grdev_session *session, grdev_display *display) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(session);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(display);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        display->framed = false;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!display->enabled || !session->enabled)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (session->n_pins > 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                display->framed = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+        else
Zbigniew Jędrzejewski-Szmek 62fe94
+                session_raise_display_frame(session, display);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+int grdev_session_new(grdev_session **out,
Zbigniew Jędrzejewski-Szmek 62fe94
+                      grdev_context *context,
Zbigniew Jędrzejewski-Szmek 62fe94
+                      unsigned int flags,
Zbigniew Jędrzejewski-Szmek 62fe94
+                      const char *name,
Zbigniew Jędrzejewski-Szmek 62fe94
+                      grdev_event_fn event_fn,
Zbigniew Jędrzejewski-Szmek 62fe94
+                      void *userdata) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        _cleanup_(grdev_session_freep) grdev_session *session = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(out);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(context);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(name);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(event_fn);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(session_id_valid(name) == !(flags & GRDEV_SESSION_CUSTOM), -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(!(flags & GRDEV_SESSION_CUSTOM) || !(flags & GRDEV_SESSION_MANAGED), -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(!(flags & GRDEV_SESSION_MANAGED) || context->sysbus, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        session = new0(grdev_session, 1);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!session)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        session->context = grdev_context_ref(context);
Zbigniew Jędrzejewski-Szmek 62fe94
+        session->custom = flags & GRDEV_SESSION_CUSTOM;
Zbigniew Jędrzejewski-Szmek 62fe94
+        session->managed = flags & GRDEV_SESSION_MANAGED;
Zbigniew Jędrzejewski-Szmek 62fe94
+        session->event_fn = event_fn;
Zbigniew Jędrzejewski-Szmek 62fe94
+        session->userdata = userdata;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        session->name = strdup(name);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!session->name)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (session->managed) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                r = sd_bus_path_encode("/org/freedesktop/login1/session",
Zbigniew Jędrzejewski-Szmek 62fe94
+                                       session->name, &session->path);
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        session->card_map = hashmap_new(&string_hash_ops);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!session->card_map)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        session->display_map = hashmap_new(&string_hash_ops);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!session->display_map)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = hashmap_put(context->session_map, session->name, session);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        *out = session;
Zbigniew Jędrzejewski-Szmek 62fe94
+        session = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+grdev_session *grdev_session_free(grdev_session *session) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_card *card;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!session)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_session_disable(session);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        while ((card = hashmap_first(session->card_map)))
Zbigniew Jędrzejewski-Szmek 62fe94
+                session_remove_card(session, card);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(hashmap_size(session->display_map) == 0);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (session->name)
Zbigniew Jędrzejewski-Szmek 62fe94
+                hashmap_remove_value(session->context->session_map, session->name, session);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        hashmap_free(session->display_map);
Zbigniew Jędrzejewski-Szmek 62fe94
+        hashmap_free(session->card_map);
Zbigniew Jędrzejewski-Szmek 62fe94
+        session->context = grdev_context_unref(session->context);
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(session->path);
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(session->name);
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(session);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+bool grdev_session_is_enabled(grdev_session *session) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        return session && session->enabled;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+void grdev_session_enable(grdev_session *session) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_card *card;
Zbigniew Jędrzejewski-Szmek 62fe94
+        Iterator iter;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(session);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!session->enabled) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                session->enabled = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+                HASHMAP_FOREACH(card, session->card_map, iter)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        grdev_card_enable(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+void grdev_session_disable(grdev_session *session) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_card *card;
Zbigniew Jędrzejewski-Szmek 62fe94
+        Iterator iter;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(session);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (session->enabled) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                session->enabled = false;
Zbigniew Jędrzejewski-Szmek 62fe94
+                HASHMAP_FOREACH(card, session->card_map, iter)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        grdev_card_disable(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+void grdev_session_commit(grdev_session *session) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_card *card;
Zbigniew Jędrzejewski-Szmek 62fe94
+        Iterator iter;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(session);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!session->enabled)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        HASHMAP_FOREACH(card, session->card_map, iter)
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (card->vtable->commit)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        card->vtable->commit(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+void grdev_session_restore(grdev_session *session) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_card *card;
Zbigniew Jędrzejewski-Szmek 62fe94
+        Iterator iter;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(session);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!session->enabled)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        HASHMAP_FOREACH(card, session->card_map, iter)
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (card->vtable->restore)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        card->vtable->restore(card);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void session_configure(grdev_session *session) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_display *display;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_tile *tile;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_card *card;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_pipe *pipe;
Zbigniew Jędrzejewski-Szmek 62fe94
+        Iterator i, j;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(session);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /*
Zbigniew Jędrzejewski-Szmek 62fe94
+         * Whenever backends add or remove pipes, we set session->modified and
Zbigniew Jędrzejewski-Szmek 62fe94
+         * require them to pin the session while modifying it. On release, we
Zbigniew Jędrzejewski-Szmek 62fe94
+         * reconfigure the device and re-assign displays to all modified pipes.
Zbigniew Jędrzejewski-Szmek 62fe94
+         *
Zbigniew Jędrzejewski-Szmek 62fe94
+         * So far, we configure each pipe as a separate display. We do not
Zbigniew Jędrzejewski-Szmek 62fe94
+         * support user-configuration, nor have we gotten any reports from
Zbigniew Jędrzejewski-Szmek 62fe94
+         * users with multi-pipe monitors (4k on DP-1.2 MST and so on). Until
Zbigniew Jędrzejewski-Szmek 62fe94
+         * we get reports, we keep the logic to a minimum.
Zbigniew Jędrzejewski-Szmek 62fe94
+         */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* create new displays for all unconfigured pipes */
Zbigniew Jędrzejewski-Szmek 62fe94
+        HASHMAP_FOREACH(card, session->card_map, i) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (!card->modified)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                card->modified = false;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                HASHMAP_FOREACH(pipe, card->pipe_map, j) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        tile = pipe->tile;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (tile->display)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        assert(!tile->parent);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        display = grdev_find_display(session, pipe->name);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (display && display->tile) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                log_debug("grdev: %s/%s: occupied display for pipe %s",
Zbigniew Jędrzejewski-Szmek 62fe94
+                                          session->name, card->name, pipe->name);
Zbigniew Jędrzejewski-Szmek 62fe94
+                                continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        } else if (!display) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                r = grdev_display_new(&display, session, pipe->name);
Zbigniew Jędrzejewski-Szmek 62fe94
+                                if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                        log_debug("grdev: %s/%s: cannot create display for pipe %s: %s",
Zbigniew Jędrzejewski-Szmek 62fe94
+                                                  session->name, card->name, pipe->name, strerror(-r));
Zbigniew Jędrzejewski-Szmek 62fe94
+                                        continue;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                }
Zbigniew Jędrzejewski-Szmek 62fe94
+                        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        tile_link(pipe->tile, display->tile);
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* update displays */
Zbigniew Jędrzejewski-Szmek 62fe94
+        HASHMAP_FOREACH(display, session->display_map, i)
Zbigniew Jędrzejewski-Szmek 62fe94
+                session_change_display(session, display);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+grdev_session *grdev_session_pin(grdev_session *session) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(session);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        ++session->n_pins;
Zbigniew Jędrzejewski-Szmek 62fe94
+        return session;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+grdev_session *grdev_session_unpin(grdev_session *session) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!session)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(session->n_pins > 0);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (--session->n_pins == 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                session_configure(session);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Contexts
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+int grdev_context_new(grdev_context **out, sd_event *event, sd_bus *sysbus) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        _cleanup_(grdev_context_unrefp) grdev_context *context = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(out, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(event, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        context = new0(grdev_context, 1);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!context)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        context->ref = 1;
Zbigniew Jędrzejewski-Szmek 62fe94
+        context->event = sd_event_ref(event);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (sysbus)
Zbigniew Jędrzejewski-Szmek 62fe94
+                context->sysbus = sd_bus_ref(sysbus);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        context->session_map = hashmap_new(&string_hash_ops);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!context->session_map)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        *out = context;
Zbigniew Jędrzejewski-Szmek 62fe94
+        context = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void context_cleanup(grdev_context *context) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(hashmap_size(context->session_map) == 0);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        hashmap_free(context->session_map);
Zbigniew Jędrzejewski-Szmek 62fe94
+        context->sysbus = sd_bus_unref(context->sysbus);
Zbigniew Jędrzejewski-Szmek 62fe94
+        context->event = sd_event_unref(context->event);
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(context);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+grdev_context *grdev_context_ref(grdev_context *context) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(context, NULL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(context->ref > 0, NULL);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        ++context->ref;
Zbigniew Jędrzejewski-Szmek 62fe94
+        return context;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+grdev_context *grdev_context_unref(grdev_context *context) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!context)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(context->ref > 0, NULL);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (--context->ref == 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                context_cleanup(context);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
diff --git a/src/libsystemd-terminal/grdev.h b/src/libsystemd-terminal/grdev.h
Zbigniew Jędrzejewski-Szmek 62fe94
new file mode 100644
Zbigniew Jędrzejewski-Szmek 62fe94
index 0000000000..2645b12113
Zbigniew Jędrzejewski-Szmek 62fe94
--- /dev/null
Zbigniew Jędrzejewski-Szmek 62fe94
+++ b/src/libsystemd-terminal/grdev.h
Zbigniew Jędrzejewski-Szmek 62fe94
@@ -0,0 +1,182 @@
Zbigniew Jędrzejewski-Szmek 62fe94
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/***
Zbigniew Jędrzejewski-Szmek 62fe94
+  This file is part of systemd.
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+  Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+  systemd is free software; you can redistribute it and/or modify it
Zbigniew Jędrzejewski-Szmek 62fe94
+  under the terms of the GNU Lesser General Public License as published by
Zbigniew Jędrzejewski-Szmek 62fe94
+  the Free Software Foundation; either version 2.1 of the License, or
Zbigniew Jędrzejewski-Szmek 62fe94
+  (at your option) any later version.
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+  systemd is distributed in the hope that it will be useful, but
Zbigniew Jędrzejewski-Szmek 62fe94
+  WITHOUT ANY WARRANTY; without even the implied warranty of
Zbigniew Jędrzejewski-Szmek 62fe94
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Zbigniew Jędrzejewski-Szmek 62fe94
+  Lesser General Public License for more details.
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+  You should have received a copy of the GNU Lesser General Public License
Zbigniew Jędrzejewski-Szmek 62fe94
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
Zbigniew Jędrzejewski-Szmek 62fe94
+***/
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Graphics Devices
Zbigniew Jędrzejewski-Szmek 62fe94
+ * The grdev layer provides generic access to graphics devices. The device
Zbigniew Jędrzejewski-Szmek 62fe94
+ * types are hidden in the implementation and exported in a generic way. The
Zbigniew Jędrzejewski-Szmek 62fe94
+ * grdev_session object forms the base layer. It loads, configures and prepares
Zbigniew Jędrzejewski-Szmek 62fe94
+ * any graphics devices associated with that session. Each session is totally
Zbigniew Jędrzejewski-Szmek 62fe94
+ * independent of other sessions and can be controlled separately.
Zbigniew Jędrzejewski-Szmek 62fe94
+ * The target devices on a session are called display. A display always
Zbigniew Jędrzejewski-Szmek 62fe94
+ * corresponds to a real display regardless how many pipes are needed to drive
Zbigniew Jędrzejewski-Szmek 62fe94
+ * that display. That is, an exported display might internally be created out
Zbigniew Jędrzejewski-Szmek 62fe94
+ * of arbitrary combinations of target pipes. However, this is meant as
Zbigniew Jędrzejewski-Szmek 62fe94
+ * implementation detail and API users must never assume details below the
Zbigniew Jędrzejewski-Szmek 62fe94
+ * display-level. That is, a display is the most low-level object exported.
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Therefore, pipe-configuration and any low-level modesetting is hidden from
Zbigniew Jędrzejewski-Szmek 62fe94
+ * the public API. It is provided by the implementation, and it is the
Zbigniew Jędrzejewski-Szmek 62fe94
+ * implementation that decides how pipes are driven.
Zbigniew Jędrzejewski-Szmek 62fe94
+ *
Zbigniew Jędrzejewski-Szmek 62fe94
+ * The API users are free to ignore specific displays or combine them to create
Zbigniew Jędrzejewski-Szmek 62fe94
+ * larger screens. This often requires user-configuration so is dictated by
Zbigniew Jędrzejewski-Szmek 62fe94
+ * policy. The underlying pipe-configuration might be affected by these
Zbigniew Jędrzejewski-Szmek 62fe94
+ * high-level policies, but is never directly controlled by those. That means,
Zbigniew Jędrzejewski-Szmek 62fe94
+ * depending on the displays you use, it might affect how underlying resources
Zbigniew Jędrzejewski-Szmek 62fe94
+ * are assigned. However, users can never directly apply policies to the pipes,
Zbigniew Jędrzejewski-Szmek 62fe94
+ * but only to displays. In case specific hardware needs quirks on the pipe
Zbigniew Jędrzejewski-Szmek 62fe94
+ * level, we support that via hwdb, not via public user configuration.
Zbigniew Jędrzejewski-Szmek 62fe94
+ *
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Right now, displays are limited to rgb32 memory-mapped framebuffers on the
Zbigniew Jędrzejewski-Szmek 62fe94
+ * primary plane. However, the grdev implementation can be easily extended to
Zbigniew Jędrzejewski-Szmek 62fe94
+ * allow more powerful access (including hardware-acceleration for 2D and 3D
Zbigniew Jędrzejewski-Szmek 62fe94
+ * compositing). So far, this wasn't needed so it is not exposed.
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+#pragma once
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <drm_fourcc.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <inttypes.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <stdbool.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <stdlib.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <systemd/sd-bus.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <systemd/sd-event.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include "util.h"
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+typedef struct grdev_fb                 grdev_fb;
Zbigniew Jędrzejewski-Szmek 62fe94
+typedef struct grdev_display_target     grdev_display_target;
Zbigniew Jędrzejewski-Szmek 62fe94
+typedef struct grdev_display            grdev_display;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+typedef struct grdev_event              grdev_event;
Zbigniew Jędrzejewski-Szmek 62fe94
+typedef struct grdev_session            grdev_session;
Zbigniew Jędrzejewski-Szmek 62fe94
+typedef struct grdev_context            grdev_context;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+enum {
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* clockwise rotation; we treat this is abelian group Z4 with ADD */
Zbigniew Jędrzejewski-Szmek 62fe94
+        GRDEV_ROTATE_0                  = 0,
Zbigniew Jędrzejewski-Szmek 62fe94
+        GRDEV_ROTATE_90                 = 1,
Zbigniew Jędrzejewski-Szmek 62fe94
+        GRDEV_ROTATE_180                = 2,
Zbigniew Jędrzejewski-Szmek 62fe94
+        GRDEV_ROTATE_270                = 3,
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+enum {
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* flip states; we treat this as abelian group V4 with XOR */
Zbigniew Jędrzejewski-Szmek 62fe94
+        GRDEV_FLIP_NONE                 = 0x0,
Zbigniew Jędrzejewski-Szmek 62fe94
+        GRDEV_FLIP_HORIZONTAL           = 0x1,
Zbigniew Jędrzejewski-Szmek 62fe94
+        GRDEV_FLIP_VERTICAL             = 0x2,
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Displays
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+struct grdev_fb {
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t width;
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t height;
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t format;
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint64_t age;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int32_t strides[4];
Zbigniew Jędrzejewski-Szmek 62fe94
+        void *maps[4];
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+struct grdev_display_target {
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t x;
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t y;
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t width;
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t height;
Zbigniew Jędrzejewski-Szmek 62fe94
+        unsigned int rotate;
Zbigniew Jędrzejewski-Szmek 62fe94
+        unsigned int flip;
Zbigniew Jędrzejewski-Szmek 62fe94
+        const grdev_fb *fb;
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+bool grdev_display_is_enabled(grdev_display *display);
Zbigniew Jędrzejewski-Szmek 62fe94
+void grdev_display_enable(grdev_display *display);
Zbigniew Jędrzejewski-Szmek 62fe94
+void grdev_display_disable(grdev_display *display);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+const grdev_display_target *grdev_display_next_target(grdev_display *display, const grdev_display_target *prev, uint64_t minage);
Zbigniew Jędrzejewski-Szmek 62fe94
+void grdev_display_flip_target(grdev_display *display, const grdev_display_target *target, uint64_t age);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+#define GRDEV_DISPLAY_FOREACH_TARGET(_display, _t, _minage)                     \
Zbigniew Jędrzejewski-Szmek 62fe94
+        for ((_t) = grdev_display_next_target((_display), NULL, (_minage));     \
Zbigniew Jędrzejewski-Szmek 62fe94
+             (_t);                                                              \
Zbigniew Jędrzejewski-Szmek 62fe94
+             (_t) = grdev_display_next_target((_display), (_t), (_minage)))
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Events
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+enum {
Zbigniew Jędrzejewski-Szmek 62fe94
+        GRDEV_EVENT_DISPLAY_ADD,
Zbigniew Jędrzejewski-Szmek 62fe94
+        GRDEV_EVENT_DISPLAY_REMOVE,
Zbigniew Jędrzejewski-Szmek 62fe94
+        GRDEV_EVENT_DISPLAY_CHANGE,
Zbigniew Jędrzejewski-Szmek 62fe94
+        GRDEV_EVENT_DISPLAY_FRAME,
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+typedef void (*grdev_event_fn) (grdev_session *session, void *userdata, grdev_event *ev);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+struct grdev_event {
Zbigniew Jędrzejewski-Szmek 62fe94
+        unsigned int type;
Zbigniew Jędrzejewski-Szmek 62fe94
+        union {
Zbigniew Jędrzejewski-Szmek 62fe94
+                struct {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        grdev_display *display;
Zbigniew Jędrzejewski-Szmek 62fe94
+                } display_add, display_remove, display_change;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                struct {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        grdev_display *display;
Zbigniew Jędrzejewski-Szmek 62fe94
+                } display_frame;
Zbigniew Jędrzejewski-Szmek 62fe94
+        };
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Sessions
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+enum {
Zbigniew Jędrzejewski-Szmek 62fe94
+        GRDEV_SESSION_CUSTOM                    = (1 << 0),
Zbigniew Jędrzejewski-Szmek 62fe94
+        GRDEV_SESSION_MANAGED                   = (1 << 1),
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+int grdev_session_new(grdev_session **out,
Zbigniew Jędrzejewski-Szmek 62fe94
+                      grdev_context *context,
Zbigniew Jędrzejewski-Szmek 62fe94
+                      unsigned int flags,
Zbigniew Jędrzejewski-Szmek 62fe94
+                      const char *name,
Zbigniew Jędrzejewski-Szmek 62fe94
+                      grdev_event_fn event_fn,
Zbigniew Jędrzejewski-Szmek 62fe94
+                      void *userdata);
Zbigniew Jędrzejewski-Szmek 62fe94
+grdev_session *grdev_session_free(grdev_session *session);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+DEFINE_TRIVIAL_CLEANUP_FUNC(grdev_session*, grdev_session_free);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+bool grdev_session_is_enabled(grdev_session *session);
Zbigniew Jędrzejewski-Szmek 62fe94
+void grdev_session_enable(grdev_session *session);
Zbigniew Jędrzejewski-Szmek 62fe94
+void grdev_session_disable(grdev_session *session);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+void grdev_session_commit(grdev_session *session);
Zbigniew Jędrzejewski-Szmek 62fe94
+void grdev_session_restore(grdev_session *session);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Contexts
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+int grdev_context_new(grdev_context **out, sd_event *event, sd_bus *sysbus);
Zbigniew Jędrzejewski-Szmek 62fe94
+grdev_context *grdev_context_ref(grdev_context *context);
Zbigniew Jędrzejewski-Szmek 62fe94
+grdev_context *grdev_context_unref(grdev_context *context);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+DEFINE_TRIVIAL_CLEANUP_FUNC(grdev_context*, grdev_context_unref);