aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTimo Teräs <timo.teras@iki.fi>2017-01-25 21:25:55 +0200
committerTimo Teräs <timo.teras@iki.fi>2017-01-25 19:28:33 +0000
commite6008f2c4d8a820d9238fbd14236e7f239848041 (patch)
treed18389e385e13ee8dd8d86568d7516ef80d7bfda
parentd66adb97be094fb7d54436775b65640599fd5176 (diff)
testing/plymouth: upgrade to git snapshot 20170123
-rw-r--r--testing/plymouth/APKBUILD22
-rw-r--r--testing/plymouth/plymouth-git-master-20170123.patch5399
2 files changed, 5415 insertions, 6 deletions
diff --git a/testing/plymouth/APKBUILD b/testing/plymouth/APKBUILD
index 3a797ec59a4..0b646bcdc9a 100644
--- a/testing/plymouth/APKBUILD
+++ b/testing/plymouth/APKBUILD
@@ -2,7 +2,7 @@
# Maintainer: Timo Teräs <timo.teras@iki.fi>
pkgname=plymouth
pkgver=0.9.2
-pkgrel=0
+pkgrel=1
pkgdesc="graphical bootsplash on linux"
url="http://www.freedesktop.org/wiki/Software/Plymouth"
arch="all"
@@ -12,14 +12,21 @@ depends_dev="pango-dev
gtk+3.0-dev
libpng-dev
libdrm-dev"
-makedepends="$depends_dev docbook-xsl"
+makedepends="$depends_dev docbook-xsl autoconf automake"
install=""
subpackages="$pkgname-dev $pkgname-doc $pkgname-themes $pkgname-x11:_render $pkgname-drm:_render"
source="http://www.freedesktop.org/software/plymouth/releases/plymouth-$pkgver.tar.bz2
- plymouth-rpmatch.patch"
+ plymouth-rpmatch.patch
+ plymouth-git-master-20170123.patch
+ "
builddir="$srcdir"/plymouth-$pkgver
+prepare() {
+ default_prepare || return 1
+ autoreconf -f -i -s
+}
+
build() {
cd "$builddir"
./configure --prefix=/usr \
@@ -51,8 +58,11 @@ _render() {
}
md5sums="ff420994deb7ea203df678df92e7ab7d plymouth-0.9.2.tar.bz2
-7d342aa2f011833647f1c696007b190c plymouth-rpmatch.patch"
+7d342aa2f011833647f1c696007b190c plymouth-rpmatch.patch
+d3451ffd12270bdbdaf563c64580addc plymouth-git-master-20170123.patch"
sha256sums="2f0ce82042cf9c7eadd2517a1f74c8a85fa8699781d9f294a06eade29fbed57f plymouth-0.9.2.tar.bz2
-2281bffcd64de8c2e675656d21b26b0a74fa997af52e941fe6f356d8d4362e37 plymouth-rpmatch.patch"
+2281bffcd64de8c2e675656d21b26b0a74fa997af52e941fe6f356d8d4362e37 plymouth-rpmatch.patch
+cdbaa71a099bbb75d669db0c581770611ebd82dc5506ae7934cb4b64afdbd7bb plymouth-git-master-20170123.patch"
sha512sums="89356eb8326504fbf3155de262ce15de0847f0a0e6d157d873cf1dea9af464a6cb9e11d7143ee9a595b217a2487060b5835eba5ac142c3cd6d66689deb272e60 plymouth-0.9.2.tar.bz2
-ec1c32ddf7ee418ef1b5d173040db464193d9bca3fd85d5c3a8d2ee13ba1218947f7c9f39c403d3ccced70be28b2c7328e82dc8f967e9bdeea1651dee4be2dc7 plymouth-rpmatch.patch"
+ec1c32ddf7ee418ef1b5d173040db464193d9bca3fd85d5c3a8d2ee13ba1218947f7c9f39c403d3ccced70be28b2c7328e82dc8f967e9bdeea1651dee4be2dc7 plymouth-rpmatch.patch
+6545c29190b9cf04df0284a73c26ec7d14fddf2c4d7a8f3dff5242b49fafda24ee2aeab54b44b19deb9228707816b6b3779ea8bec7b742020cb1f7e2ca64b509 plymouth-git-master-20170123.patch"
diff --git a/testing/plymouth/plymouth-git-master-20170123.patch b/testing/plymouth/plymouth-git-master-20170123.patch
new file mode 100644
index 00000000000..6ab412fa5d1
--- /dev/null
+++ b/testing/plymouth/plymouth-git-master-20170123.patch
@@ -0,0 +1,5399 @@
+diff --git a/Makefile.am b/Makefile.am
+index 395c91b..cb6eedb 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -4,7 +4,7 @@ if BUILD_DOCUMENTATION
+ SUBDIRS += docs
+ endif
+
+-DISTCHECK_CONFIGURE_FLAGS = --disable-tests --without-system-root-install
++DISTCHECK_CONFIGURE_FLAGS = --disable-tests --disable-systemd-integration
+
+ EXTRA_DIST = ChangeLog \
+ README
+diff --git a/configure.ac b/configure.ac
+index aad673e..d145c69 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -1,4 +1,4 @@
+-AC_INIT([plymouth],[0.9.2],[https://bugs.freedesktop.org/enter_bug.cgi?product=plymouth])
++AC_INIT([plymouth],[0.9.3],[https://bugs.freedesktop.org/enter_bug.cgi?product=plymouth])
+ AC_CONFIG_SRCDIR(src/main.c)
+ AC_CONFIG_HEADER(config.h)
+ AC_CONFIG_AUX_DIR(build-tools)
+@@ -11,7 +11,7 @@ AM_PROG_CC_C_O
+ AC_HEADER_STDC
+ AC_C_CONST
+
+-AM_INIT_AUTOMAKE([dist-bzip2 no-dist-gzip])
++AM_INIT_AUTOMAKE([dist-xz no-dist-gzip])
+ m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+ AM_MAINTAINER_MODE([enable])
+
+@@ -38,9 +38,18 @@ PKG_CHECK_MODULES(IMAGE, [libpng >= 1.2.16 ])
+ AC_SUBST(IMAGE_CFLAGS)
+ AC_SUBST(IMAGE_LIBS)
+
+-PKG_CHECK_MODULES(UDEV, [libudev]);
+-AC_SUBST(UDEV_CFLAGS)
+-AC_SUBST(UDEV_LIBS)
++AC_ARG_WITH(udev, AS_HELP_STRING([--with-udev], [Add udev support]),, with_udev=yes)
++
++if test "x$with_udev" != "xno" ; then
++ PKG_CHECK_MODULES(UDEV, [libudev], have_udev=yes, have_udev=no)
++ AC_SUBST(UDEV_CFLAGS)
++ AC_SUBST(UDEV_LIBS)
++ if test "x$have_udev" = "xyes"; then
++ AC_DEFINE(HAVE_UDEV, 1, [Define if have udev support])
++ else
++ AC_MSG_ERROR([libudev is required unless --without-udev is passed])
++ fi
++fi
+
+ PLYMOUTH_CFLAGS=""
+ PLYMOUTH_LIBS="-lm -lrt -ldl"
+@@ -117,16 +126,21 @@ if test x$enable_upstart_monitoring = xyes; then
+ fi
+ AM_CONDITIONAL(ENABLE_UPSTART_MONITORING, [test "$enable_upstart_monitoring" = yes])
+
+-AC_ARG_ENABLE(systemd-integration, AS_HELP_STRING([--enable-systemd-integration],[coordinate boot up with systemd]),enable_systemd_integration=$enableval,enable_systemd_integration=no)
++AC_ARG_ENABLE(systemd-integration, AS_HELP_STRING([--enable-systemd-integration],[coordinate boot up with systemd]),enable_systemd_integration=$enableval,enable_systemd_integration=yes)
+ AM_CONDITIONAL(ENABLE_SYSTEMD_INTEGRATION, [test "$enable_systemd_integration" = yes])
+
+ if test x$enable_systemd_integration = xyes; then
+ AC_DEFINE(PLY_ENABLE_SYSTEMD_INTEGRATION, 1, [Coordinate boot up with systemd])
+- SYSTEMD_UNIT_DIR=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)
++ AC_ARG_WITH([systemdunitdir], AC_HELP_STRING([--with-systemdunitdir=DIR],
++ [path to systemd service directory]), [path_systemdunit=${withval}],
++ [path_systemdunit="`$PKG_CONFIG --variable=systemdsystemunitdir systemd`"])
++ if (test -n "${path_systemdunit}"); then
++ SYSTEMD_UNIT_DIR="${path_systemdunit}"
++ fi
+ AC_SUBST(SYSTEMD_UNIT_DIR)
+ fi
+
+-AC_ARG_WITH(system-root-install, AS_HELP_STRING([--with-system-root-install],[Install client in /bin and daemon in /sbin]),with_system_root_install=${withval},with_system_root_install=yes)
++AC_ARG_WITH(system-root-install, AS_HELP_STRING([--with-system-root-install],[Install client in /bin and daemon in /sbin]),with_system_root_install=${withval},with_system_root_install=no)
+ AM_CONDITIONAL(WITH_SYSTEM_ROOT_INSTALL, [test "$with_system_root_install" = yes])
+
+ if test x$with_system_root_install = xyes; then
+diff --git a/scripts/plymouth-populate-initrd.in b/scripts/plymouth-populate-initrd.in
+index 43c7f22..e3326e9 100755
+--- a/scripts/plymouth-populate-initrd.in
++++ b/scripts/plymouth-populate-initrd.in
+@@ -392,6 +392,9 @@ fi
+ if [ $THEME_OVERRIDE ]; then
+ conf=$INITRDDIR/${PLYMOUTH_CONFDIR}/plymouthd.conf
+ echo "modifying plymouthd.conf: Theme=$PLYMOUTH_THEME_NAME" > /dev/stderr
++ # make sure the section and key exist so we can modify them
++ grep -q "^ *\[Daemon\]" $conf || echo "[Daemon]" >> $conf
++ grep -q "^ *Theme *=" $conf || echo "Theme=fade-in" >> $conf
+ sed -i "s/^ *Theme *=.*/# theme modified by plymouth-populate-initrd\nTheme=$PLYMOUTH_THEME_NAME/" $conf
+ fi
+
+diff --git a/src/libply-splash-core/Makefile.am b/src/libply-splash-core/Makefile.am
+index d07d7f1..7036569 100644
+--- a/src/libply-splash-core/Makefile.am
++++ b/src/libply-splash-core/Makefile.am
+@@ -21,7 +21,6 @@ libply_splash_core_HEADERS = \
+ ply-pixel-display.h \
+ ply-renderer.h \
+ ply-renderer-plugin.h \
+- ply-seat.h \
+ ply-terminal.h \
+ ply-text-display.h \
+ ply-text-progress-bar.h \
+@@ -47,7 +46,6 @@ libply_splash_core_la_SOURCES = \
+ ply-terminal.c \
+ ply-pixel-buffer.c \
+ ply-renderer.c \
+- ply-seat.c \
+ ply-boot-splash.c
+
+ MAINTAINERCLEANFILES = Makefile.in
+diff --git a/src/libply-splash-core/ply-boot-splash.c b/src/libply-splash-core/ply-boot-splash.c
+index 310d4d5..87a7a0c 100644
+--- a/src/libply-splash-core/ply-boot-splash.c
++++ b/src/libply-splash-core/ply-boot-splash.c
+@@ -57,7 +57,10 @@ struct _ply_boot_splash
+ ply_boot_splash_mode_t mode;
+ ply_buffer_t *boot_buffer;
+ ply_trigger_t *idle_trigger;
+- ply_list_t *seats;
++
++ ply_keyboard_t *keyboard;
++ ply_list_t *pixel_displays;
++ ply_list_t *text_displays;
+
+ char *theme_path;
+ char *plugin_dir;
+@@ -94,160 +97,102 @@ ply_boot_splash_new (const char *theme_path,
+ splash->mode = PLY_BOOT_SPLASH_MODE_INVALID;
+
+ splash->boot_buffer = boot_buffer;
+- splash->seats = ply_list_new ();
++ splash->pixel_displays = ply_list_new ();
++ splash->text_displays = ply_list_new ();
+
+ return splash;
+ }
+
+-static void
+-detach_from_seat (ply_boot_splash_t *splash,
+- ply_seat_t *seat)
++void
++ply_boot_splash_set_keyboard (ply_boot_splash_t *splash,
++ ply_keyboard_t *keyboard)
+ {
+- ply_keyboard_t *keyboard;
+- ply_list_t *displays;
+- ply_list_node_t *node, *next_node;
+-
+- ply_trace ("removing keyboard");
+- if (splash->plugin_interface->unset_keyboard != NULL) {
+- keyboard = ply_seat_get_keyboard (seat);
+- splash->plugin_interface->unset_keyboard (splash->plugin, keyboard);
+- }
+-
+- ply_trace ("removing pixel displays");
+- displays = ply_seat_get_pixel_displays (seat);
+-
+- node = ply_list_get_first_node (displays);
+- while (node != NULL) {
+- ply_pixel_display_t *display;
+- ply_list_node_t *next_node;
+- unsigned long width, height;
+-
+- display = ply_list_node_get_data (node);
+- next_node = ply_list_get_next_node (displays, node);
+-
+- width = ply_pixel_display_get_width (display);
+- height = ply_pixel_display_get_height (display);
+-
+- ply_trace ("Removing %lux%lu pixel display", width, height);
+-
+- if (splash->plugin_interface->remove_pixel_display != NULL)
+- splash->plugin_interface->remove_pixel_display (splash->plugin, display);
+-
+- node = next_node;
+- }
+-
+- ply_trace ("removing text displays");
+- displays = ply_seat_get_text_displays (seat);
+-
+- node = ply_list_get_first_node (displays);
+- while (node != NULL) {
+- ply_text_display_t *display;
+- int number_of_columns, number_of_rows;
+-
+- display = ply_list_node_get_data (node);
+- next_node = ply_list_get_next_node (displays, node);
+-
+- number_of_columns = ply_text_display_get_number_of_columns (display);
+- number_of_rows = ply_text_display_get_number_of_rows (display);
+-
+- ply_trace ("Removing %dx%d text display", number_of_columns, number_of_rows);
+-
+- if (splash->plugin_interface->remove_text_display != NULL)
+- splash->plugin_interface->remove_text_display (splash->plugin, display);
++ if (splash->plugin_interface->set_keyboard == NULL)
++ return;
+
+- node = next_node;
+- }
++ splash->plugin_interface->set_keyboard (splash->plugin, keyboard);
++ splash->keyboard = keyboard;
+ }
+
+-static void
+-attach_to_seat (ply_boot_splash_t *splash,
+- ply_seat_t *seat)
++void
++ply_boot_splash_unset_keyboard (ply_boot_splash_t *splash)
+ {
+- ply_keyboard_t *keyboard;
+- ply_list_t *displays;
+- ply_list_node_t *node, *next_node;
+-
+- if (splash->plugin_interface->set_keyboard != NULL) {
+- keyboard = ply_seat_get_keyboard (seat);
+- splash->plugin_interface->set_keyboard (splash->plugin, keyboard);
+- }
+-
+- if (splash->plugin_interface->add_pixel_display != NULL) {
+- displays = ply_seat_get_pixel_displays (seat);
+-
+- ply_trace ("adding pixel displays");
+- node = ply_list_get_first_node (displays);
+- while (node != NULL) {
+- ply_pixel_display_t *display;
+- ply_list_node_t *next_node;
+- unsigned long width, height;
+-
+- display = ply_list_node_get_data (node);
+- next_node = ply_list_get_next_node (displays, node);
++ if (splash->plugin_interface->unset_keyboard == NULL)
++ return;
+
+- width = ply_pixel_display_get_width (display);
+- height = ply_pixel_display_get_height (display);
++ splash->plugin_interface->unset_keyboard (splash->plugin, splash->keyboard);
++}
+
+- ply_trace ("Adding %lux%lu pixel display", width, height);
++void
++ply_boot_splash_add_pixel_display (ply_boot_splash_t *splash,
++ ply_pixel_display_t *display)
++{
++ unsigned long width, height;
+
+- splash->plugin_interface->add_pixel_display (splash->plugin, display);
++ if (splash->plugin_interface->add_pixel_display == NULL)
++ return;
+
+- node = next_node;
+- }
+- }
++ width = ply_pixel_display_get_width (display);
++ height = ply_pixel_display_get_height (display);
+
+- if (splash->plugin_interface->add_text_display != NULL) {
+- displays = ply_seat_get_text_displays (seat);
++ ply_trace ("adding %lux%lu pixel display", width, height);
+
+- ply_trace ("adding text displays");
+- node = ply_list_get_first_node (displays);
+- while (node != NULL) {
+- ply_text_display_t *display;
+- int number_of_columns, number_of_rows;
++ splash->plugin_interface->add_pixel_display (splash->plugin, display);
++ ply_list_append_data (splash->pixel_displays, display);
++}
+
+- display = ply_list_node_get_data (node);
+- next_node = ply_list_get_next_node (displays, node);
++void
++ply_boot_splash_remove_pixel_display (ply_boot_splash_t *splash,
++ ply_pixel_display_t *display)
++{
++ unsigned long width, height;
+
+- number_of_columns = ply_text_display_get_number_of_columns (display);
+- number_of_rows = ply_text_display_get_number_of_rows (display);
++ if (splash->plugin_interface->remove_pixel_display == NULL)
++ return;
+
+- ply_trace ("Adding %dx%d text display", number_of_columns, number_of_rows);
++ width = ply_pixel_display_get_width (display);
++ height = ply_pixel_display_get_height (display);
+
+- splash->plugin_interface->add_text_display (splash->plugin, display);
++ ply_trace ("removing %lux%lu pixel display", width, height);
+
+- node = next_node;
+- }
+- }
++ splash->plugin_interface->remove_pixel_display (splash->plugin, display);
++ ply_list_remove_data (splash->pixel_displays, display);
+ }
+
+ void
+-ply_boot_splash_attach_to_seat (ply_boot_splash_t *splash,
+- ply_seat_t *seat)
++ply_boot_splash_add_text_display (ply_boot_splash_t *splash,
++ ply_text_display_t *display)
+ {
+- ply_list_node_t *node;
++ int number_of_columns, number_of_rows;
+
+- node = ply_list_find_node (splash->seats, seat);
+-
+- if (node != NULL)
++ if (splash->plugin_interface->add_text_display == NULL)
+ return;
+
+- ply_list_append_data (splash->seats, seat);
+- attach_to_seat (splash, seat);
++ number_of_columns = ply_text_display_get_number_of_columns (display);
++ number_of_rows = ply_text_display_get_number_of_rows (display);
++
++ ply_trace ("adding %dx%d text display", number_of_columns, number_of_rows);
++
++ splash->plugin_interface->add_text_display (splash->plugin, display);
++ ply_list_append_data (splash->text_displays, display);
+ }
+
+ void
+-ply_boot_splash_detach_from_seat (ply_boot_splash_t *splash,
+- ply_seat_t *seat)
++ply_boot_splash_remove_text_display (ply_boot_splash_t *splash,
++ ply_text_display_t *display)
+ {
+- ply_list_node_t *node;
+-
+- node = ply_list_find_node (splash->seats, seat);
++ int number_of_columns, number_of_rows;
+
+- if (node == NULL)
++ if (splash->plugin_interface->remove_text_display == NULL)
+ return;
+
+- ply_list_remove_data (splash->seats, seat);
+- detach_from_seat (splash, seat);
++ number_of_columns = ply_text_display_get_number_of_columns (display);
++ number_of_rows = ply_text_display_get_number_of_rows (display);
++
++ ply_trace ("removing %dx%d text display", number_of_columns, number_of_rows);
++
++ splash->plugin_interface->remove_text_display (splash->plugin, display);
++ ply_list_remove_data (splash->text_displays, display);
+ }
+
+ bool
+@@ -378,23 +323,60 @@ ply_boot_splash_unload (ply_boot_splash_t *splash)
+ }
+
+ static void
+-detach_from_seats (ply_boot_splash_t *splash)
++remove_pixel_displays (ply_boot_splash_t *splash)
++{
++ ply_list_node_t *node;
++
++ if (splash->plugin_interface->remove_pixel_display == NULL)
++ return;
++
++ ply_trace ("removing pixel displays");
++
++ node = ply_list_get_first_node (splash->pixel_displays);
++ while (node != NULL) {
++ ply_pixel_display_t *display;
++ ply_list_node_t *next_node;
++ unsigned long width, height;
++
++ display = ply_list_node_get_data (node);
++ next_node = ply_list_get_next_node (splash->pixel_displays, node);
++
++ width = ply_pixel_display_get_width (display);
++ height = ply_pixel_display_get_height (display);
++
++ ply_trace ("Removing %lux%lu pixel display", width, height);
++
++ splash->plugin_interface->remove_pixel_display (splash->plugin, display);
++
++ node = next_node;
++ }
++}
++
++static void
++remove_text_displays (ply_boot_splash_t *splash)
+ {
+ ply_list_node_t *node;
+
+- ply_trace ("detaching from seats");
++ if (splash->plugin_interface->remove_text_display == NULL)
++ return;
++
++ ply_trace ("removing text displays");
+
+- node = ply_list_get_first_node (splash->seats);
++ node = ply_list_get_first_node (splash->text_displays);
+ while (node != NULL) {
+- ply_seat_t *seat;
++ ply_text_display_t *display;
+ ply_list_node_t *next_node;
++ int number_of_columns, number_of_rows;
+
+- seat = ply_list_node_get_data (node);
+- next_node = ply_list_get_next_node (splash->seats, node);
++ display = ply_list_node_get_data (node);
++ next_node = ply_list_get_next_node (splash->text_displays, node);
+
+- detach_from_seat (splash, seat);
++ number_of_columns = ply_text_display_get_number_of_columns (display);
++ number_of_rows = ply_text_display_get_number_of_rows (display);
++
++ ply_trace ("Removing %dx%d text display", number_of_columns, number_of_rows);
+
+- ply_list_remove_node (splash->seats, node);
++ splash->plugin_interface->remove_text_display (splash->plugin, display);
+
+ node = next_node;
+ }
+@@ -419,11 +401,17 @@ ply_boot_splash_free (ply_boot_splash_t *splash)
+ splash);
+ }
+
+- detach_from_seats (splash);
+- ply_list_free (splash->seats);
++ if (splash->module_handle != NULL) {
++ ply_boot_splash_unset_keyboard (splash);
++
++ remove_pixel_displays (splash);
++ ply_list_free (splash->pixel_displays);
++
++ remove_text_displays (splash);
++ ply_list_free (splash->text_displays);
+
+- if (splash->module_handle != NULL)
+ ply_boot_splash_unload (splash);
++ }
+
+ if (splash->idle_trigger != NULL)
+ ply_trigger_free (splash->idle_trigger);
+diff --git a/src/libply-splash-core/ply-boot-splash.h b/src/libply-splash-core/ply-boot-splash.h
+index 0ad6f22..b66ca47 100644
+--- a/src/libply-splash-core/ply-boot-splash.h
++++ b/src/libply-splash-core/ply-boot-splash.h
+@@ -33,12 +33,10 @@
+ #include "ply-pixel-display.h"
+ #include "ply-text-display.h"
+ #include "ply-progress.h"
+-#include "ply-seat.h"
+
+ #include "ply-boot-splash-plugin.h"
+
+ typedef struct _ply_boot_splash ply_boot_splash_t;
+-typedef struct _ply_seat ply_seat_t;
+
+ typedef void (*ply_boot_splash_on_idle_handler_t) (void *user_data);
+
+@@ -50,10 +48,17 @@ ply_boot_splash_t *ply_boot_splash_new (const char *theme_path,
+ bool ply_boot_splash_load (ply_boot_splash_t *splash);
+ bool ply_boot_splash_load_built_in (ply_boot_splash_t *splash);
+ void ply_boot_splash_unload (ply_boot_splash_t *splash);
+-void ply_boot_splash_attach_to_seat (ply_boot_splash_t *splash,
+- ply_seat_t *seat);
+-void ply_boot_splash_detach_from_seat (ply_boot_splash_t *splash,
+- ply_seat_t *seat);
++void ply_boot_splash_set_keyboard (ply_boot_splash_t *splash,
++ ply_keyboard_t *keyboard);
++void ply_boot_splash_unset_keyboard (ply_boot_splash_t *splash);
++void ply_boot_splash_add_pixel_display (ply_boot_splash_t *splash,
++ ply_pixel_display_t *pixel_display);
++void ply_boot_splash_remove_pixel_display (ply_boot_splash_t *splash,
++ ply_pixel_display_t *pixel_display);
++void ply_boot_splash_add_text_display (ply_boot_splash_t *splash,
++ ply_text_display_t *text_display);
++void ply_boot_splash_remove_text_display (ply_boot_splash_t *splash,
++ ply_text_display_t *text_display);
+ void ply_boot_splash_free (ply_boot_splash_t *splash);
+ bool ply_boot_splash_show (ply_boot_splash_t *splash,
+ ply_boot_splash_mode_t mode);
+diff --git a/src/libply-splash-core/ply-device-manager.c b/src/libply-splash-core/ply-device-manager.c
+index 67eba32..b4c33d4 100644
+--- a/src/libply-splash-core/ply-device-manager.c
++++ b/src/libply-splash-core/ply-device-manager.c
+@@ -30,7 +30,9 @@
+ #include <sys/stat.h>
+ #include <sys/types.h>
+
++#ifdef HAVE_UDEV
+ #include <libudev.h>
++#endif
+
+ #include "ply-logger.h"
+ #include "ply-event-loop.h"
+@@ -41,27 +43,40 @@
+ #define SUBSYSTEM_DRM "drm"
+ #define SUBSYSTEM_FRAME_BUFFER "graphics"
+
+-static void create_seat_for_terminal_and_renderer_type (ply_device_manager_t *manager,
+- const char *device_path,
+- ply_terminal_t *terminal,
+- ply_renderer_type_t renderer_type);
++#ifdef HAVE_UDEV
++static void create_devices_from_udev (ply_device_manager_t *manager);
++#endif
++
++static void create_devices_for_terminal_and_renderer_type (ply_device_manager_t *manager,
++ const char *device_path,
++ ply_terminal_t *terminal,
++ ply_renderer_type_t renderer_type);
+ struct _ply_device_manager
+ {
+ ply_device_manager_flags_t flags;
+ ply_event_loop_t *loop;
+ ply_hashtable_t *terminals;
++ ply_hashtable_t *renderers;
+ ply_terminal_t *local_console_terminal;
+- ply_seat_t *local_console_seat;
+- ply_list_t *seats;
++ ply_list_t *keyboards;
++ ply_list_t *text_displays;
++ ply_list_t *pixel_displays;
+ struct udev *udev_context;
+- struct udev_queue *udev_queue;
+- int udev_queue_fd;
+- ply_fd_watch_t *udev_queue_fd_watch;
+ struct udev_monitor *udev_monitor;
+
+- ply_seat_added_handler_t seat_added_handler;
+- ply_seat_removed_handler_t seat_removed_handler;
+- void *seat_event_handler_data;
++ ply_keyboard_added_handler_t keyboard_added_handler;
++ ply_keyboard_removed_handler_t keyboard_removed_handler;
++ ply_pixel_display_added_handler_t pixel_display_added_handler;
++ ply_pixel_display_removed_handler_t pixel_display_removed_handler;
++ ply_text_display_added_handler_t text_display_added_handler;
++ ply_text_display_removed_handler_t text_display_removed_handler;
++ void *event_handler_data;
++
++ uint32_t local_console_managed : 1;
++ uint32_t local_console_is_text : 1;
++ uint32_t serial_consoles_detected : 1;
++ uint32_t renderers_activated : 1;
++ uint32_t keyboards_activated : 1;
+ };
+
+ static void
+@@ -87,64 +102,66 @@ attach_to_event_loop (ply_device_manager_t *manager,
+ manager);
+ }
+
+-static bool
+-device_is_for_local_console (ply_device_manager_t *manager,
+- struct udev_device *device)
+-{
+- const char *device_path;
+- struct udev_device *bus_device;
+- char *bus_device_path;
+- const char *boot_vga;
+- bool for_local_console;
+-
+- /* Look at the associated bus device to see if this card is the
+- * card the kernel is using for its console. */
+- device_path = udev_device_get_syspath (device);
+- asprintf (&bus_device_path, "%s/device", device_path);
+- bus_device = udev_device_new_from_syspath (manager->udev_context, bus_device_path);
+-
+- boot_vga = udev_device_get_sysattr_value (bus_device, "boot_vga");
+- free (bus_device_path);
+-
+- if (boot_vga != NULL && strcmp (boot_vga, "1") == 0)
+- for_local_console = true;
+- else
+- for_local_console = false;
+-
+- return for_local_console;
+-}
+-
+-static bool
+-drm_device_in_use (ply_device_manager_t *manager,
+- const char *device_path)
++static void
++free_displays_for_renderer (ply_device_manager_t *manager,
++ ply_renderer_t *renderer)
+ {
+ ply_list_node_t *node;
+
+- node = ply_list_get_first_node (manager->seats);
++ node = ply_list_get_first_node (manager->pixel_displays);
+ while (node != NULL) {
+- ply_seat_t *seat;
+- ply_renderer_t *renderer;
+ ply_list_node_t *next_node;
+- const char *renderer_device_path;
++ ply_pixel_display_t *display;
++ ply_renderer_t *display_renderer;
+
+- seat = ply_list_node_get_data (node);
+- next_node = ply_list_get_next_node (manager->seats, node);
+- renderer = ply_seat_get_renderer (seat);
++ display = ply_list_node_get_data (node);
++ next_node = ply_list_get_next_node (manager->pixel_displays, node);
++ display_renderer = ply_pixel_display_get_renderer (display);
+
+- if (renderer != NULL) {
+- renderer_device_path = ply_renderer_get_device_name (renderer);
++ if (display_renderer == renderer) {
++ if (manager->pixel_display_removed_handler != NULL)
++ manager->pixel_display_removed_handler (manager->event_handler_data, display);
++ ply_pixel_display_free (display);
++ ply_list_remove_node (manager->pixel_displays, node);
+
+- if (renderer_device_path != NULL) {
+- if (strcmp (device_path, renderer_device_path) == 0) {
+- return true;
+- }
+- }
+ }
+
+ node = next_node;
+ }
++}
+
+- return false;
++static void
++free_devices_from_device_path (ply_device_manager_t *manager,
++ const char *device_path)
++{
++ char *key = NULL;
++ ply_renderer_t *renderer = NULL;
++
++ ply_hashtable_lookup_full (manager->renderers,
++ (void *) device_path,
++ (void **) &key,
++ (void **) &renderer);
++
++ if (renderer == NULL)
++ return;
++
++ free_displays_for_renderer (manager, renderer);
++
++ ply_hashtable_remove (manager->renderers, (void *) device_path);
++ free (key);
++ ply_renderer_free (renderer);
++}
++
++#ifdef HAVE_UDEV
++static bool
++drm_device_in_use (ply_device_manager_t *manager,
++ const char *device_path)
++{
++ ply_renderer_t *renderer;
++
++ renderer = ply_hashtable_lookup (manager->renderers, (void *) device_path);
++
++ return renderer != NULL;
+ }
+
+ static bool
+@@ -196,19 +213,10 @@ fb_device_has_drm_device (ply_device_manager_t *manager,
+ }
+
+ static void
+-create_seat_for_udev_device (ply_device_manager_t *manager,
+- struct udev_device *device)
++create_devices_for_udev_device (ply_device_manager_t *manager,
++ struct udev_device *device)
+ {
+- bool for_local_console;
+ const char *device_path;
+- ply_terminal_t *terminal = NULL;
+-
+- for_local_console = device_is_for_local_console (manager, device);
+-
+- ply_trace ("device is for local console: %s", for_local_console ? "yes" : "no");
+-
+- if (for_local_console)
+- terminal = manager->local_console_terminal;
+
+ device_path = udev_device_get_devnode (device);
+
+@@ -231,7 +239,13 @@ create_seat_for_udev_device (ply_device_manager_t *manager,
+ }
+
+ if (renderer_type != PLY_RENDERER_TYPE_NONE) {
+- create_seat_for_terminal_and_renderer_type (manager,
++ ply_terminal_t *terminal = NULL;
++
++ if (!manager->local_console_managed) {
++ terminal = manager->local_console_terminal;
++ }
++
++ create_devices_for_terminal_and_renderer_type (manager,
+ device_path,
+ terminal,
+ renderer_type);
+@@ -240,64 +254,26 @@ create_seat_for_udev_device (ply_device_manager_t *manager,
+ }
+
+ static void
+-free_seat_from_device_path (ply_device_manager_t *manager,
+- const char *device_path)
+-{
+- ply_list_node_t *node;
+-
+- node = ply_list_get_first_node (manager->seats);
+- while (node != NULL) {
+- ply_seat_t *seat;
+- ply_renderer_t *renderer;
+- ply_list_node_t *next_node;
+- const char *renderer_device_path;
+-
+- seat = ply_list_node_get_data (node);
+- next_node = ply_list_get_next_node (manager->seats, node);
+- renderer = ply_seat_get_renderer (seat);
+-
+- if (renderer != NULL) {
+- renderer_device_path = ply_renderer_get_device_name (renderer);
+-
+- if (renderer_device_path != NULL) {
+- if (strcmp (device_path, renderer_device_path) == 0) {
+- ply_trace ("removing seat associated with %s", device_path);
+-
+- if (manager->seat_removed_handler != NULL)
+- manager->seat_removed_handler (manager->seat_event_handler_data, seat);
+-
+- ply_seat_free (seat);
+- ply_list_remove_node (manager->seats, node);
+- break;
+- }
+- }
+- }
+-
+- node = next_node;
+- }
+-}
+-
+-static void
+-free_seat_for_udev_device (ply_device_manager_t *manager,
+- struct udev_device *device)
++free_devices_for_udev_device (ply_device_manager_t *manager,
++ struct udev_device *device)
+ {
+ const char *device_path;
+
+ device_path = udev_device_get_devnode (device);
+
+ if (device_path != NULL)
+- free_seat_from_device_path (manager, device_path);
++ free_devices_from_device_path (manager, device_path);
+ }
+
+ static bool
+-create_seats_for_subsystem (ply_device_manager_t *manager,
+- const char *subsystem)
++create_devices_for_subsystem (ply_device_manager_t *manager,
++ const char *subsystem)
+ {
+ struct udev_enumerate *matches;
+ struct udev_list_entry *entry;
+ bool found_device = false;
+
+- ply_trace ("creating seats for %s devices",
++ ply_trace ("creating objects for %s devices",
+ strcmp (subsystem, SUBSYSTEM_FRAME_BUFFER) == 0 ?
+ "frame buffer" :
+ subsystem);
+@@ -326,7 +302,7 @@ create_seats_for_subsystem (ply_device_manager_t *manager,
+ if (udev_device_get_is_initialized (device)) {
+ ply_trace ("device is initialized");
+
+- /* We only care about devices assigned to a (any) seat. Floating
++ /* We only care about devices assigned to a (any) devices. Floating
+ * devices should be ignored.
+ */
+ if (udev_device_has_tag (device, "seat")) {
+@@ -335,10 +311,10 @@ create_seats_for_subsystem (ply_device_manager_t *manager,
+ if (node != NULL) {
+ ply_trace ("found node %s", node);
+ found_device = true;
+- create_seat_for_udev_device (manager, device);
++ create_devices_for_udev_device (manager, device);
+ }
+ } else {
+- ply_trace ("device doesn't have a seat tag");
++ ply_trace ("device doesn't have a devices tag");
+ }
+ } else {
+ ply_trace ("it's not initialized");
+@@ -371,17 +347,19 @@ on_udev_event (ply_device_manager_t *manager)
+
+ if (strcmp (action, "add") == 0) {
+ const char *subsystem;
+- bool coldplug_complete = manager->udev_queue_fd_watch == NULL;
+
+ subsystem = udev_device_get_subsystem (device);
+
+- if (strcmp (subsystem, SUBSYSTEM_DRM) == 0 ||
+- coldplug_complete)
+- create_seat_for_udev_device (manager, device);
+- else
+- ply_trace ("ignoring since we only handle subsystem %s devices after coldplug completes", subsystem);
++ if (strcmp (subsystem, SUBSYSTEM_DRM) == 0) {
++ if (manager->local_console_managed && manager->local_console_is_text)
++ ply_trace ("ignoring since we're already using text splash for local console");
++ else
++ create_devices_for_udev_device (manager, device);
++ } else {
++ ply_trace ("ignoring since we only handle subsystem %s devices after timeout", subsystem);
++ }
+ } else if (strcmp (action, "remove") == 0) {
+- free_seat_for_udev_device (manager, device);
++ free_devices_for_udev_device (manager, device);
+ }
+
+ udev_device_unref (device);
+@@ -413,30 +391,7 @@ watch_for_udev_events (ply_device_manager_t *manager)
+ NULL,
+ manager);
+ }
+-
+-static void
+-free_seats (ply_device_manager_t *manager)
+-{
+- ply_list_node_t *node;
+-
+- ply_trace ("removing seats");
+- node = ply_list_get_first_node (manager->seats);
+- while (node != NULL) {
+- ply_seat_t *seat;
+- ply_list_node_t *next_node;
+-
+- seat = ply_list_node_get_data (node);
+- next_node = ply_list_get_next_node (manager->seats, node);
+-
+- if (manager->seat_removed_handler != NULL)
+- manager->seat_removed_handler (manager->seat_event_handler_data, seat);
+-
+- ply_seat_free (seat);
+- ply_list_remove_node (manager->seats, node);
+-
+- node = next_node;
+- }
+-}
++#endif
+
+ static void
+ free_terminal (char *device,
+@@ -473,6 +428,10 @@ get_terminal (ply_device_manager_t *manager,
+ strcmp (full_name, "/dev/tty") == 0 ||
+ strcmp (full_name, ply_terminal_get_name (manager->local_console_terminal)) == 0) {
+ terminal = manager->local_console_terminal;
++
++ ply_hashtable_insert (manager->terminals,
++ (void *) ply_terminal_get_name (terminal),
++ terminal);
+ goto done;
+ }
+
+@@ -491,6 +450,23 @@ done:
+ return terminal;
+ }
+
++static void
++free_renderer (char *device_path,
++ ply_renderer_t *renderer,
++ ply_device_manager_t *manager)
++{
++ free_devices_from_device_path (manager, device_path);
++}
++
++static void
++free_renderers (ply_device_manager_t *manager)
++{
++ ply_hashtable_foreach (manager->renderers,
++ (ply_hashtable_foreach_func_t *)
++ free_renderer,
++ manager);
++}
++
+ ply_device_manager_t *
+ ply_device_manager_new (const char *default_tty,
+ ply_device_manager_flags_t flags)
+@@ -500,15 +476,19 @@ ply_device_manager_new (const char *default_tty,
+ manager = calloc (1, sizeof(ply_device_manager_t));
+ manager->loop = NULL;
+ manager->terminals = ply_hashtable_new (ply_hashtable_string_hash, ply_hashtable_string_compare);
++ manager->renderers = ply_hashtable_new (ply_hashtable_string_hash, ply_hashtable_string_compare);
+ manager->local_console_terminal = ply_terminal_new (default_tty);
+- ply_hashtable_insert (manager->terminals,
+- (void *) ply_terminal_get_name (manager->local_console_terminal),
+- manager->local_console_terminal);
+- manager->seats = ply_list_new ();
++ manager->keyboards = ply_list_new ();
++ manager->text_displays = ply_list_new ();
++ manager->pixel_displays = ply_list_new ();
+ manager->flags = flags;
+
++#ifdef HAVE_UDEV
+ if (!(flags & PLY_DEVICE_MANAGER_FLAGS_IGNORE_UDEV))
+ manager->udev_context = udev_new ();
++#else
++ manager->flags |= PLY_DEVICE_MANAGER_FLAGS_IGNORE_UDEV;
++#endif
+
+ attach_to_event_loop (manager, ply_event_loop_get_default ());
+
+@@ -527,17 +507,24 @@ ply_device_manager_free (ply_device_manager_t *manager)
+ (ply_event_loop_exit_handler_t)
+ detach_from_event_loop,
+ manager);
+- free_seats (manager);
+- ply_list_free (manager->seats);
+
+ free_terminals (manager);
+ ply_hashtable_free (manager->terminals);
+
++ free_renderers (manager);
++ ply_hashtable_free (manager->renderers);
++
++#ifdef HAVE_UDEV
++ ply_event_loop_stop_watching_for_timeout (manager->loop,
++ (ply_event_loop_timeout_handler_t)
++ create_devices_from_udev, manager);
++
+ if (manager->udev_monitor != NULL)
+ udev_monitor_unref (manager->udev_monitor);
+
+ if (manager->udev_context != NULL)
+ udev_unref (manager->udev_context);
++#endif
+
+ free (manager);
+ }
+@@ -615,53 +602,161 @@ add_consoles_from_file (ply_device_manager_t *manager,
+ }
+
+ static void
+-create_seat_for_terminal_and_renderer_type (ply_device_manager_t *manager,
+- const char *device_path,
+- ply_terminal_t *terminal,
+- ply_renderer_type_t renderer_type)
++create_pixel_displays_for_renderer (ply_device_manager_t *manager,
++ ply_renderer_t *renderer)
+ {
+- ply_seat_t *seat;
+- bool is_local_terminal = false;
++ ply_list_t *heads;
++ ply_list_node_t *node;
++
++ heads = ply_renderer_get_heads (renderer);
+
+- if (terminal != NULL && manager->local_console_terminal == terminal)
+- is_local_terminal = true;
++ ply_trace ("Adding displays for %d heads",
++ ply_list_get_length (heads));
++
++ node = ply_list_get_first_node (heads);
++ while (node != NULL) {
++ ply_list_node_t *next_node;
++ ply_renderer_head_t *head;
++ ply_pixel_display_t *display;
++
++ head = ply_list_node_get_data (node);
++ next_node = ply_list_get_next_node (heads, node);
++
++ display = ply_pixel_display_new (renderer, head);
++
++ ply_list_append_data (manager->pixel_displays, display);
++
++ if (manager->pixel_display_added_handler != NULL)
++ manager->pixel_display_added_handler (manager->event_handler_data, display);
++ node = next_node;
++ }
++}
++
++static void
++create_text_displays_for_terminal (ply_device_manager_t *manager,
++ ply_terminal_t *terminal)
++{
++ ply_text_display_t *display;
+
+- if (is_local_terminal && manager->local_console_seat != NULL) {
+- ply_trace ("trying to create seat for local console when one already exists");
++ if (!ply_terminal_is_open (terminal)) {
++ if (!ply_terminal_open (terminal)) {
++ ply_trace ("could not add terminal %s: %m",
++ ply_terminal_get_name (terminal));
++ return;
++ }
++ }
++
++ ply_trace ("adding text display for terminal %s",
++ ply_terminal_get_name (terminal));
++
++ display = ply_text_display_new (terminal);
++ ply_list_append_data (manager->text_displays, display);
++
++ if (manager->text_display_added_handler != NULL)
++ manager->text_display_added_handler (manager->event_handler_data, display);
++}
++
++static void
++create_devices_for_terminal_and_renderer_type (ply_device_manager_t *manager,
++ const char *device_path,
++ ply_terminal_t *terminal,
++ ply_renderer_type_t renderer_type)
++{
++ ply_renderer_t *renderer = NULL;
++ ply_keyboard_t *keyboard = NULL;
++
++ if (device_path != NULL)
++ renderer = ply_hashtable_lookup (manager->renderers, (void *) device_path);
++
++ if (renderer != NULL) {
++ ply_trace ("ignoring device %s since it's already managed", device_path);
+ return;
+ }
+
+- ply_trace ("creating seat for %s (renderer type: %u) (terminal: %s)",
++ ply_trace ("creating devices for %s (renderer type: %u) (terminal: %s)",
+ device_path ? : "", renderer_type, terminal ? ply_terminal_get_name (terminal) : "none");
+- seat = ply_seat_new (terminal);
+
+- if (!ply_seat_open (seat, renderer_type, device_path)) {
+- ply_trace ("could not create seat");
+- ply_seat_free (seat);
+- return;
++ if (renderer_type != PLY_RENDERER_TYPE_NONE) {
++ ply_renderer_t *old_renderer = NULL;
++ renderer = ply_renderer_new (renderer_type, device_path, terminal);
++
++ if (renderer != NULL && !ply_renderer_open (renderer)) {
++ ply_trace ("could not open renderer for %s", device_path);
++ ply_renderer_free (renderer);
++ renderer = NULL;
++
++ if (renderer_type != PLY_RENDERER_TYPE_AUTO)
++ return;
++ }
++
++ if (renderer != NULL) {
++ old_renderer = ply_hashtable_lookup (manager->renderers,
++ (void *) ply_renderer_get_device_name (renderer));
++
++ if (old_renderer != NULL) {
++ ply_trace ("ignoring device %s since it's alerady managed",
++ ply_renderer_get_device_name (renderer));
++ ply_renderer_free (renderer);
++
++ renderer = NULL;
++ return;
++ }
++ }
++ }
++
++ if (renderer != NULL) {
++ keyboard = ply_keyboard_new_for_renderer (renderer);
++ ply_list_append_data (manager->keyboards, keyboard);
++
++ if (manager->keyboard_added_handler != NULL)
++ manager->keyboard_added_handler (manager->event_handler_data, keyboard);
++
++ create_pixel_displays_for_renderer (manager, renderer);
++ ply_hashtable_insert (manager->renderers, strdup (ply_renderer_get_device_name (renderer)), renderer);
++ create_pixel_displays_for_renderer (manager, renderer);
++
++ if (manager->renderers_activated) {
++ ply_trace ("activating renderer");
++ ply_renderer_activate (renderer);
++ }
++
++ if (terminal != NULL)
++ ply_terminal_refresh_geometry (terminal);
++ } else if (terminal != NULL) {
++ keyboard = ply_keyboard_new_for_terminal (terminal);
++ ply_list_append_data (manager->keyboards, keyboard);
++
++ if (manager->keyboard_added_handler != NULL)
++ manager->keyboard_added_handler (manager->event_handler_data, keyboard);
+ }
+
+- ply_list_append_data (manager->seats, seat);
++ if (terminal != NULL) {
++ create_text_displays_for_terminal (manager, terminal);
+
+- if (is_local_terminal)
+- manager->local_console_seat = seat;
++ if (terminal == manager->local_console_terminal) {
++ manager->local_console_is_text = renderer == NULL;
++ manager->local_console_managed = true;
++ }
++ }
+
+- if (manager->seat_added_handler != NULL)
+- manager->seat_added_handler (manager->seat_event_handler_data, seat);
++ if (keyboard != NULL && manager->keyboards_activated) {
++ ply_trace ("activating keyboards");
++ ply_keyboard_watch_for_input (keyboard);
++ }
+ }
+
+ static void
+-create_seat_for_terminal (const char *device_path,
+- ply_terminal_t *terminal,
+- ply_device_manager_t *manager)
++create_devices_for_terminal (const char *device_path,
++ ply_terminal_t *terminal,
++ ply_device_manager_t *manager)
+ {
+- create_seat_for_terminal_and_renderer_type (manager,
+- device_path,
+- terminal,
+- PLY_RENDERER_TYPE_NONE);
++ create_devices_for_terminal_and_renderer_type (manager,
++ NULL,
++ terminal,
++ PLY_RENDERER_TYPE_NONE);
+ }
+ static bool
+-create_seats_from_terminals (ply_device_manager_t *manager)
++create_devices_from_terminals (ply_device_manager_t *manager)
+ {
+ bool has_serial_consoles;
+
+@@ -676,9 +771,11 @@ create_seats_from_terminals (ply_device_manager_t *manager)
+
+ if (has_serial_consoles) {
+ ply_trace ("serial consoles detected, managing them with details forced");
++ manager->serial_consoles_detected = true;
++
+ ply_hashtable_foreach (manager->terminals,
+ (ply_hashtable_foreach_func_t *)
+- create_seat_for_terminal,
++ create_devices_for_terminal,
+ manager);
+ return true;
+ }
+@@ -686,145 +783,104 @@ create_seats_from_terminals (ply_device_manager_t *manager)
+ return false;
+ }
+
++#ifdef HAVE_UDEV
+ static void
+-create_seats_from_udev (ply_device_manager_t *manager)
++create_devices_from_udev (ply_device_manager_t *manager)
+ {
+ bool found_drm_device, found_fb_device;
+
+- ply_trace ("Looking for devices from udev");
++ ply_trace ("Timeout elapsed, looking for devices from udev");
+
+- found_drm_device = create_seats_for_subsystem (manager, SUBSYSTEM_DRM);
+- found_fb_device = create_seats_for_subsystem (manager, SUBSYSTEM_FRAME_BUFFER);
++ found_drm_device = create_devices_for_subsystem (manager, SUBSYSTEM_DRM);
++ found_fb_device = create_devices_for_subsystem (manager, SUBSYSTEM_FRAME_BUFFER);
+
+ if (found_drm_device || found_fb_device)
+ return;
+
+- ply_trace ("Creating non-graphical seat, since there's no suitable graphics hardware");
+- create_seat_for_terminal_and_renderer_type (manager,
+- ply_terminal_get_name (manager->local_console_terminal),
+- manager->local_console_terminal,
+- PLY_RENDERER_TYPE_NONE);
+-}
+-
+-static void
+-create_fallback_seat (ply_device_manager_t *manager)
+-{
+- create_seat_for_terminal_and_renderer_type (manager,
+- ply_terminal_get_name (manager->local_console_terminal),
+- manager->local_console_terminal,
+- PLY_RENDERER_TYPE_AUTO);
++ ply_trace ("Creating non-graphical devices, since there's no suitable graphics hardware");
++ create_devices_for_terminal_and_renderer_type (manager,
++ NULL,
++ manager->local_console_terminal,
++ PLY_RENDERER_TYPE_NONE);
+ }
++#endif
+
+ static void
+-on_udev_queue_changed (ply_device_manager_t *manager)
++create_fallback_devices (ply_device_manager_t *manager)
+ {
+- if (!udev_queue_get_queue_is_empty (manager->udev_queue))
+- return;
+-
+- ply_trace ("udev coldplug complete");
+- ply_event_loop_stop_watching_fd (manager->loop, manager->udev_queue_fd_watch);
+- manager->udev_queue_fd_watch = NULL;
+- udev_queue_unref (manager->udev_queue);
+-
+- close (manager->udev_queue_fd);
+- manager->udev_queue_fd = -1;
+-
+- manager->udev_queue = NULL;
+-
+- create_seats_from_udev (manager);
+-}
+-
+-static void
+-watch_for_coldplug_completion (ply_device_manager_t *manager)
+-{
+- int fd;
+- int result;
+-
+- manager->udev_queue = udev_queue_new (manager->udev_context);
+-
+- if (udev_queue_get_queue_is_empty (manager->udev_queue)) {
+- ply_trace ("udev coldplug completed already ");
+- create_seats_from_udev (manager);
+- return;
+- }
+-
+- fd = inotify_init1 (IN_CLOEXEC);
+- result = inotify_add_watch (fd, "/run/udev", IN_MOVED_TO| IN_DELETE);
+-
+- if (result < 0) {
+- ply_trace ("could not watch for udev to show up: %m");
+- close (fd);
+-
+- create_fallback_seat (manager);
+- return;
+- }
+-
+- manager->udev_queue_fd = fd;
+-
+- manager->udev_queue_fd_watch = ply_event_loop_watch_fd (manager->loop,
+- fd,
+- PLY_EVENT_LOOP_FD_STATUS_HAS_DATA,
+- (ply_event_handler_t)
+- on_udev_queue_changed,
+- NULL,
+- manager);
++ create_devices_for_terminal_and_renderer_type (manager,
++ NULL,
++ manager->local_console_terminal,
++ PLY_RENDERER_TYPE_AUTO);
+ }
+
+ void
+-ply_device_manager_watch_seats (ply_device_manager_t *manager,
+- ply_seat_added_handler_t seat_added_handler,
+- ply_seat_removed_handler_t seat_removed_handler,
+- void *data)
+-{
+- bool done_with_initial_seat_setup;
+-
+- manager->seat_added_handler = seat_added_handler;
+- manager->seat_removed_handler = seat_removed_handler;
+- manager->seat_event_handler_data = data;
+-
+- /* Try to create seats for each serial device right away, if possible
++ply_device_manager_watch_devices (ply_device_manager_t *manager,
++ double device_timeout,
++ ply_keyboard_added_handler_t keyboard_added_handler,
++ ply_keyboard_removed_handler_t keyboard_removed_handler,
++ ply_pixel_display_added_handler_t pixel_display_added_handler,
++ ply_pixel_display_removed_handler_t pixel_display_removed_handler,
++ ply_text_display_added_handler_t text_display_added_handler,
++ ply_text_display_removed_handler_t text_display_removed_handler,
++ void *data)
++{
++ bool done_with_initial_devices_setup;
++
++ manager->keyboard_added_handler = keyboard_added_handler;
++ manager->keyboard_removed_handler = keyboard_removed_handler;
++ manager->pixel_display_added_handler = pixel_display_added_handler;
++ manager->pixel_display_removed_handler = pixel_display_removed_handler;
++ manager->text_display_added_handler = text_display_added_handler;
++ manager->text_display_removed_handler = text_display_removed_handler;
++ manager->event_handler_data = data;
++
++ /* Try to create devices for each serial device right away, if possible
+ */
+- done_with_initial_seat_setup = create_seats_from_terminals (manager);
++ done_with_initial_devices_setup = create_devices_from_terminals (manager);
+
+- if (done_with_initial_seat_setup)
++ if (done_with_initial_devices_setup)
+ return;
+
+ if ((manager->flags & PLY_DEVICE_MANAGER_FLAGS_IGNORE_UDEV)) {
+- ply_trace ("udev support disabled, creating fallback seat");
+- create_fallback_seat (manager);
++ ply_trace ("udev support disabled, creating fallback devices");
++ create_fallback_devices (manager);
+ return;
+ }
+
++#ifdef HAVE_UDEV
+ watch_for_udev_events (manager);
+- watch_for_coldplug_completion (manager);
++ create_devices_for_subsystem (manager, SUBSYSTEM_DRM);
++ ply_event_loop_watch_for_timeout (manager->loop,
++ device_timeout,
++ (ply_event_loop_timeout_handler_t)
++ create_devices_from_udev, manager);
++#endif
+ }
+
+ bool
+-ply_device_manager_has_open_seats (ply_device_manager_t *manager)
++ply_device_manager_has_displays (ply_device_manager_t *manager)
+ {
+- ply_list_node_t *node;
+-
+- node = ply_list_get_first_node (manager->seats);
+- while (node != NULL) {
+- ply_seat_t *seat;
+- ply_list_node_t *next_node;
+-
+- seat = ply_list_node_get_data (node);
+- next_node = ply_list_get_next_node (manager->seats, node);
+-
+- if (ply_seat_is_open (seat))
+- return true;
++ return ply_list_get_length (manager->pixel_displays) > 0 ||
++ ply_list_get_length (manager->text_displays) > 0;
++}
+
+- node = next_node;
+- }
++ply_list_t *
++ply_device_manager_get_keyboards (ply_device_manager_t *manager)
++{
++ return manager->keyboards;
++}
+
+- return false;
++ply_list_t *
++ply_device_manager_get_pixel_displays (ply_device_manager_t *manager)
++{
++ return manager->pixel_displays;
+ }
+
+ ply_list_t *
+-ply_device_manager_get_seats (ply_device_manager_t *manager)
++ply_device_manager_get_text_displays (ply_device_manager_t *manager)
+ {
+- return manager->seats;
++ return manager->text_displays;
+ }
+
+ ply_terminal_t *
+@@ -833,44 +889,49 @@ ply_device_manager_get_default_terminal (ply_device_manager_t *manager)
+ return manager->local_console_terminal;
+ }
+
++bool
++ply_device_manager_has_serial_consoles (ply_device_manager_t *manager)
++{
++ return manager->serial_consoles_detected;
++}
++
++static void
++activate_renderer (char *device_path,
++ ply_renderer_t *renderer,
++ ply_device_manager_t *manager)
++{
++ ply_renderer_activate (renderer);
++}
++
+ void
+ ply_device_manager_activate_renderers (ply_device_manager_t *manager)
+ {
+- ply_list_node_t *node;
+-
+ ply_trace ("activating renderers");
+- node = ply_list_get_first_node (manager->seats);
+- while (node != NULL) {
+- ply_seat_t *seat;
+- ply_list_node_t *next_node;
+-
+- seat = ply_list_node_get_data (node);
+- next_node = ply_list_get_next_node (manager->seats, node);
++ ply_hashtable_foreach (manager->renderers,
++ (ply_hashtable_foreach_func_t *)
++ activate_renderer,
++ manager);
+
+- ply_seat_activate_renderer (seat);
++ manager->renderers_activated = true;
++}
+
+- node = next_node;
+- }
++static void
++deactivate_renderer (char *device_path,
++ ply_renderer_t *renderer,
++ ply_device_manager_t *manager)
++{
++ ply_renderer_deactivate (renderer);
+ }
+
+ void
+ ply_device_manager_deactivate_renderers (ply_device_manager_t *manager)
+ {
+- ply_list_node_t *node;
+-
+- ply_trace ("deactivating renderers");
+- node = ply_list_get_first_node (manager->seats);
+- while (node != NULL) {
+- ply_seat_t *seat;
+- ply_list_node_t *next_node;
+-
+- seat = ply_list_node_get_data (node);
+- next_node = ply_list_get_next_node (manager->seats, node);
+-
+- ply_seat_deactivate_renderer (seat);
++ ply_hashtable_foreach (manager->renderers,
++ (ply_hashtable_foreach_func_t *)
++ deactivate_renderer,
++ manager);
+
+- node = next_node;
+- }
++ manager->renderers_activated = false;
+ }
+
+ void
+@@ -879,18 +940,20 @@ ply_device_manager_activate_keyboards (ply_device_manager_t *manager)
+ ply_list_node_t *node;
+
+ ply_trace ("activating keyboards");
+- node = ply_list_get_first_node (manager->seats);
++ node = ply_list_get_first_node (manager->keyboards);
+ while (node != NULL) {
+- ply_seat_t *seat;
++ ply_keyboard_t *keyboard;
+ ply_list_node_t *next_node;
+
+- seat = ply_list_node_get_data (node);
+- next_node = ply_list_get_next_node (manager->seats, node);
++ keyboard = ply_list_node_get_data (node);
++ next_node = ply_list_get_next_node (manager->keyboards, node);
+
+- ply_seat_activate_keyboard (seat);
++ ply_keyboard_watch_for_input (keyboard);
+
+ node = next_node;
+ }
++
++ manager->keyboards_activated = true;
+ }
+
+ void
+@@ -899,16 +962,18 @@ ply_device_manager_deactivate_keyboards (ply_device_manager_t *manager)
+ ply_list_node_t *node;
+
+ ply_trace ("deactivating keyboards");
+- node = ply_list_get_first_node (manager->seats);
++ node = ply_list_get_first_node (manager->keyboards);
+ while (node != NULL) {
+- ply_seat_t *seat;
++ ply_keyboard_t *keyboard;
+ ply_list_node_t *next_node;
+
+- seat = ply_list_node_get_data (node);
+- next_node = ply_list_get_next_node (manager->seats, node);
++ keyboard = ply_list_node_get_data (node);
++ next_node = ply_list_get_next_node (manager->keyboards, node);
+
+- ply_seat_deactivate_keyboard (seat);
++ ply_keyboard_stop_watching_for_input (keyboard);
+
+ node = next_node;
+ }
++
++ manager->keyboards_activated = false;
+ }
+diff --git a/src/libply-splash-core/ply-device-manager.h b/src/libply-splash-core/ply-device-manager.h
+index c3e6487..058f6e8 100644
+--- a/src/libply-splash-core/ply-device-manager.h
++++ b/src/libply-splash-core/ply-device-manager.h
+@@ -21,7 +21,11 @@
+ #define PLY_DEVICE_MANAGER_H
+
+ #include <stdbool.h>
+-#include "ply-seat.h"
++
++#include "ply-keyboard.h"
++#include "ply-pixel-display.h"
++#include "ply-renderer.h"
++#include "ply-text-display.h"
+
+ typedef enum
+ {
+@@ -31,20 +35,30 @@ typedef enum
+ } ply_device_manager_flags_t;
+
+ typedef struct _ply_device_manager ply_device_manager_t;
+-typedef void (*ply_seat_added_handler_t) (void *,
+- ply_seat_t *);
+-typedef void (*ply_seat_removed_handler_t) (void *,
+- ply_seat_t *);
++typedef void (* ply_keyboard_added_handler_t) (void *, ply_keyboard_t *);
++typedef void (* ply_keyboard_removed_handler_t) (void *, ply_keyboard_t *);
++typedef void (* ply_pixel_display_added_handler_t) (void *, ply_pixel_display_t *);
++typedef void (* ply_pixel_display_removed_handler_t) (void *, ply_pixel_display_t *);
++typedef void (* ply_text_display_added_handler_t) (void *, ply_text_display_t *);
++typedef void (* ply_text_display_removed_handler_t) (void *, ply_text_display_t *);
+
+ #ifndef PLY_HIDE_FUNCTION_DECLARATIONS
+ ply_device_manager_t *ply_device_manager_new (const char *default_tty,
+ ply_device_manager_flags_t flags);
+-void ply_device_manager_watch_seats (ply_device_manager_t *manager,
+- ply_seat_added_handler_t seat_added_handler,
+- ply_seat_removed_handler_t seat_removed_handler,
+- void *data);
+-bool ply_device_manager_has_open_seats (ply_device_manager_t *manager);
+-ply_list_t *ply_device_manager_get_seats (ply_device_manager_t *manager);
++void ply_device_manager_watch_devices (ply_device_manager_t *manager,
++ double device_timeout,
++ ply_keyboard_added_handler_t keyboard_added_handler,
++ ply_keyboard_removed_handler_t keyboard_removed_handler,
++ ply_pixel_display_added_handler_t pixel_display_added_handler,
++ ply_pixel_display_removed_handler_t pixel_display_removed_handler,
++ ply_text_display_added_handler_t text_display_added_handler,
++ ply_text_display_removed_handler_t text_display_removed_handler,
++ void *data);
++bool ply_device_manager_has_serial_consoles (ply_device_manager_t *manager);
++bool ply_device_manager_has_displays (ply_device_manager_t *manager);
++ply_list_t *ply_device_manager_get_keyboards (ply_device_manager_t *manager);
++ply_list_t *ply_device_manager_get_pixel_displays (ply_device_manager_t *manager);
++ply_list_t *ply_device_manager_get_text_displays (ply_device_manager_t *manager);
+ void ply_device_manager_free (ply_device_manager_t *manager);
+ void ply_device_manager_activate_keyboards (ply_device_manager_t *manager);
+ void ply_device_manager_deactivate_keyboards (ply_device_manager_t *manager);
+diff --git a/src/libply-splash-core/ply-keyboard.c b/src/libply-splash-core/ply-keyboard.c
+index ccf08e8..80f7694 100644
+--- a/src/libply-splash-core/ply-keyboard.c
++++ b/src/libply-splash-core/ply-keyboard.c
+@@ -93,6 +93,8 @@ struct _ply_keyboard
+ ply_list_t *backspace_handler_list;
+ ply_list_t *escape_handler_list;
+ ply_list_t *enter_handler_list;
++
++ uint32_t is_active : 1;
+ };
+
+ static bool ply_keyboard_watch_for_terminal_input (ply_keyboard_t *keyboard);
+@@ -323,6 +325,12 @@ ply_keyboard_stop_watching_for_renderer_input (ply_keyboard_t *keyboard)
+ keyboard->provider.if_renderer->input_source);
+ }
+
++bool
++ply_keyboard_is_active (ply_keyboard_t *keyboard)
++{
++ return keyboard->is_active;
++}
++
+ static void
+ on_terminal_data (ply_keyboard_t *keyboard)
+ {
+@@ -369,15 +377,20 @@ ply_keyboard_watch_for_input (ply_keyboard_t *keyboard)
+ {
+ assert (keyboard != NULL);
+
++ if (keyboard->is_active)
++ return true;
++
+ switch (keyboard->provider_type) {
+ case PLY_KEYBOARD_PROVIDER_TYPE_RENDERER:
+- return ply_keyboard_watch_for_renderer_input (keyboard);
++ keyboard->is_active = ply_keyboard_watch_for_renderer_input (keyboard);
++ break;
+
+ case PLY_KEYBOARD_PROVIDER_TYPE_TERMINAL:
+- return ply_keyboard_watch_for_terminal_input (keyboard);
++ keyboard->is_active = ply_keyboard_watch_for_terminal_input (keyboard);
++ break;
+ }
+
+- return false;
++ return keyboard->is_active;
+ }
+
+ void
+@@ -385,6 +398,9 @@ ply_keyboard_stop_watching_for_input (ply_keyboard_t *keyboard)
+ {
+ assert (keyboard != NULL);
+
++ if (!keyboard->is_active)
++ return;
++
+ switch (keyboard->provider_type) {
+ case PLY_KEYBOARD_PROVIDER_TYPE_RENDERER:
+ ply_keyboard_stop_watching_for_renderer_input (keyboard);
+@@ -394,6 +410,8 @@ ply_keyboard_stop_watching_for_input (ply_keyboard_t *keyboard)
+ ply_keyboard_stop_watching_for_terminal_input (keyboard);
+ break;
+ }
++
++ keyboard->is_active = false;
+ }
+
+ void
+diff --git a/src/libply-splash-core/ply-keyboard.h b/src/libply-splash-core/ply-keyboard.h
+index 23497d9..6147cc7 100644
+--- a/src/libply-splash-core/ply-keyboard.h
++++ b/src/libply-splash-core/ply-keyboard.h
+@@ -71,6 +71,7 @@ void ply_keyboard_remove_enter_handler (ply_keyboard_t *keyboard,
+
+ bool ply_keyboard_watch_for_input (ply_keyboard_t *keyboard);
+ void ply_keyboard_stop_watching_for_input (ply_keyboard_t *keyboard);
++bool ply_keyboard_is_active (ply_keyboard_t *keyboard);
+
+ #endif
+
+diff --git a/src/libply-splash-core/ply-pixel-buffer.c b/src/libply-splash-core/ply-pixel-buffer.c
+index c1825cc..52a3f86 100644
+--- a/src/libply-splash-core/ply-pixel-buffer.c
++++ b/src/libply-splash-core/ply-pixel-buffer.c
+@@ -37,14 +37,19 @@
+ #include <stdlib.h>
+ #include <unistd.h>
+
++#define ALPHA_MASK 0xff000000
++
+ struct _ply_pixel_buffer
+ {
+ uint32_t *bytes;
+
+- ply_rectangle_t area;
+- ply_list_t *clip_areas;
++ ply_rectangle_t area; /* in device pixels */
++ ply_rectangle_t logical_area; /* in logical pixels */
++ ply_list_t *clip_areas; /* in device pixels */
+
+- ply_region_t *updated_areas;
++ ply_region_t *updated_areas; /* in device pixels */
++ uint32_t is_opaque : 1;
++ int device_scale;
+ };
+
+ static inline void ply_pixel_buffer_blend_value_at_pixel (ply_pixel_buffer_t *buffer,
+@@ -166,6 +171,34 @@ ply_pixel_buffer_blend_value_at_pixel (ply_pixel_buffer_t *buffer,
+ }
+
+ static void
++ply_rectangle_upscale (ply_rectangle_t *area,
++ int scale)
++{
++ area->x *= scale;
++ area->y *= scale;
++ area->width *= scale;
++ area->height *= scale;
++}
++
++static void
++ply_rectangle_downscale (ply_rectangle_t *area,
++ int scale)
++{
++ area->x /= scale;
++ area->y /= scale;
++ area->width /= scale;
++ area->height /= scale;
++}
++
++static void
++ply_pixel_buffer_adjust_area_for_device_scale (ply_pixel_buffer_t *buffer,
++ ply_rectangle_t *area)
++{
++ ply_rectangle_upscale (area, buffer->device_scale);
++}
++
++/* this function will also convert logical pixels to device pixels */
++static void
+ ply_pixel_buffer_crop_area_to_clip_area (ply_pixel_buffer_t *buffer,
+ ply_rectangle_t *area,
+ ply_rectangle_t *cropped_area)
+@@ -173,6 +206,7 @@ ply_pixel_buffer_crop_area_to_clip_area (ply_pixel_buffer_t *buffer,
+ ply_list_node_t *node;
+
+ *cropped_area = *area;
++ ply_pixel_buffer_adjust_area_for_device_scale (buffer, cropped_area);
+
+ node = ply_list_get_first_node (buffer->clip_areas);
+ while (node != NULL) {
+@@ -196,8 +230,19 @@ ply_pixel_buffer_fill_area_with_pixel_value (ply_pixel_buffer_t *buffer,
+ unsigned long row, column;
+ ply_rectangle_t cropped_area;
+
++ if (fill_area == NULL)
++ fill_area = &buffer->logical_area;
++
+ ply_pixel_buffer_crop_area_to_clip_area (buffer, fill_area, &cropped_area);
+
++ /* If we're filling the entire buffer with a fully opaque color,
++ * then make note of it
++ */
++ if (fill_area == &buffer->area &&
++ (pixel_value >> 24) == 0xff) {
++ buffer->is_opaque = true;
++ }
++
+ for (row = cropped_area.y; row < cropped_area.y + cropped_area.height; row++) {
+ for (column = cropped_area.x; column < cropped_area.x + cropped_area.width; column++) {
+ ply_pixel_buffer_blend_value_at_pixel (buffer,
+@@ -205,6 +250,8 @@ ply_pixel_buffer_fill_area_with_pixel_value (ply_pixel_buffer_t *buffer,
+ pixel_value);
+ }
+ }
++
++ ply_region_add_rectangle (buffer->updated_areas, &cropped_area);
+ }
+
+ void
+@@ -216,6 +263,8 @@ ply_pixel_buffer_push_clip_area (ply_pixel_buffer_t *buffer,
+ new_clip_area = malloc (sizeof(*new_clip_area));
+
+ *new_clip_area = *clip_area;
++ ply_pixel_buffer_adjust_area_for_device_scale (buffer, new_clip_area);
++
+ ply_list_append_data (buffer->clip_areas, new_clip_area);
+ }
+
+@@ -241,9 +290,12 @@ ply_pixel_buffer_new (unsigned long width,
+ buffer->bytes = (uint32_t *) calloc (height, width * sizeof(uint32_t));
+ buffer->area.width = width;
+ buffer->area.height = height;
++ buffer->logical_area = buffer->area;
++ buffer->device_scale = 1;
+
+ buffer->clip_areas = ply_list_new ();
+ ply_pixel_buffer_push_clip_area (buffer, &buffer->area);
++ buffer->is_opaque = false;
+
+ return buffer;
+ }
+@@ -278,21 +330,36 @@ ply_pixel_buffer_get_size (ply_pixel_buffer_t *buffer,
+ assert (buffer != NULL);
+ assert (size != NULL);
+
+- *size = buffer->area;
++ *size = buffer->logical_area;
+ }
+
+ unsigned long
+ ply_pixel_buffer_get_width (ply_pixel_buffer_t *buffer)
+ {
+ assert (buffer != NULL);
+- return buffer->area.width;
++ return buffer->logical_area.width;
+ }
+
+ unsigned long
+ ply_pixel_buffer_get_height (ply_pixel_buffer_t *buffer)
+ {
+ assert (buffer != NULL);
+- return buffer->area.height;
++ return buffer->logical_area.height;
++}
++
++bool
++ply_pixel_buffer_is_opaque (ply_pixel_buffer_t *buffer)
++{
++ assert (buffer != NULL);
++ return buffer->is_opaque;
++}
++
++void
++ply_pixel_buffer_set_opaque (ply_pixel_buffer_t *buffer,
++ bool is_opaque)
++{
++ assert (buffer != NULL);
++ buffer->is_opaque = is_opaque;
+ }
+
+ ply_region_t *
+@@ -359,7 +426,7 @@ ply_pixel_buffer_fill_with_gradient (ply_pixel_buffer_t *buffer,
+ ply_rectangle_t cropped_area;
+
+ if (fill_area == NULL)
+- fill_area = &buffer->area;
++ fill_area = &buffer->logical_area;
+
+ ply_pixel_buffer_crop_area_to_clip_area (buffer, fill_area, &cropped_area);
+
+@@ -430,24 +497,16 @@ ply_pixel_buffer_fill_with_color (ply_pixel_buffer_t *buffer,
+ double alpha)
+ {
+ uint32_t pixel_value;
+- ply_rectangle_t cropped_area;
+
+ assert (buffer != NULL);
+
+- if (fill_area == NULL)
+- fill_area = &buffer->area;
+-
+- ply_pixel_buffer_crop_area_to_clip_area (buffer, fill_area, &cropped_area);
+-
+ red *= alpha;
+ green *= alpha;
+ blue *= alpha;
+
+ pixel_value = PLY_PIXEL_BUFFER_COLOR_TO_PIXEL_VALUE (red, green, blue, alpha);
+
+- ply_pixel_buffer_fill_area_with_pixel_value (buffer, &cropped_area, pixel_value);
+-
+- ply_region_add_rectangle (buffer->updated_areas, &cropped_area);
++ ply_pixel_buffer_fill_area_with_pixel_value (buffer, fill_area, pixel_value);
+ }
+
+ void
+@@ -456,7 +515,6 @@ ply_pixel_buffer_fill_with_hex_color_at_opacity (ply_pixel_buffer_t *buffer,
+ uint32_t hex_color,
+ double opacity)
+ {
+- ply_rectangle_t cropped_area;
+ uint32_t pixel_value;
+ double red;
+ double green;
+@@ -465,11 +523,6 @@ ply_pixel_buffer_fill_with_hex_color_at_opacity (ply_pixel_buffer_t *buffer,
+
+ assert (buffer != NULL);
+
+- if (fill_area == NULL)
+- fill_area = &buffer->area;
+-
+- ply_pixel_buffer_crop_area_to_clip_area (buffer, fill_area, &cropped_area);
+-
+ /* if they only gave an rgb hex number, assume an alpha of 0xff
+ */
+ if ((hex_color & 0xff000000) == 0)
+@@ -488,9 +541,7 @@ ply_pixel_buffer_fill_with_hex_color_at_opacity (ply_pixel_buffer_t *buffer,
+
+ pixel_value = PLY_PIXEL_BUFFER_COLOR_TO_PIXEL_VALUE (red, green, blue, alpha);
+
+- ply_pixel_buffer_fill_area_with_pixel_value (buffer, &cropped_area, pixel_value);
+-
+- ply_region_add_rectangle (buffer->updated_areas, &cropped_area);
++ ply_pixel_buffer_fill_area_with_pixel_value (buffer, fill_area, pixel_value);
+ }
+
+ void
+@@ -502,48 +553,120 @@ ply_pixel_buffer_fill_with_hex_color (ply_pixel_buffer_t *buffer,
+ hex_color, 1.0);
+ }
+
++static inline uint32_t
++ply_pixels_interpolate (uint32_t *bytes,
++ int width,
++ int height,
++ double x,
++ double y)
++{
++ int ix;
++ int iy;
++ int i;
++ int offset_x;
++ int offset_y;
++ uint32_t pixels[2][2];
++ uint32_t reply = 0;
++
++ for (offset_y = 0; offset_y < 2; offset_y++) {
++ for (offset_x = 0; offset_x < 2; offset_x++) {
++ ix = x + offset_x;
++ iy = y + offset_y;
++
++ if (ix < 0 || ix >= width || iy < 0 || iy >= height)
++ pixels[offset_y][offset_x] = 0x00000000;
++ else
++ pixels[offset_y][offset_x] = bytes[ix + iy * width];
++ }
++ }
++ if (!pixels[0][0] && !pixels[0][1] && !pixels[1][0] && !pixels[1][1]) return 0;
++
++ ix = x;
++ iy = y;
++ x -= ix;
++ y -= iy;
++ for (i = 0; i < 4; i++) {
++ uint32_t value = 0;
++ uint32_t mask = 0xFF << (i * 8);
++ value += ((pixels[0][0]) & mask) * (1 - x) * (1 - y);
++ value += ((pixels[0][1]) & mask) * x * (1 - y);
++ value += ((pixels[1][0]) & mask) * (1 - x) * y;
++ value += ((pixels[1][1]) & mask) * x * y;
++ reply |= value & mask;
++ }
++ return reply;
++}
++
+ void
+-ply_pixel_buffer_fill_with_argb32_data_at_opacity_with_clip (ply_pixel_buffer_t *buffer,
+- ply_rectangle_t *fill_area,
+- ply_rectangle_t *clip_area,
+- uint32_t *data,
+- double opacity)
++ply_pixel_buffer_fill_with_argb32_data_at_opacity_with_clip_and_scale (ply_pixel_buffer_t *buffer,
++ ply_rectangle_t *fill_area,
++ ply_rectangle_t *clip_area,
++ uint32_t *data,
++ double opacity,
++ int scale)
+ {
+ unsigned long row, column;
+ uint8_t opacity_as_byte;
++ ply_rectangle_t logical_fill_area;
+ ply_rectangle_t cropped_area;
+ unsigned long x;
+ unsigned long y;
++ double scale_factor;
+
+ assert (buffer != NULL);
+
+- if (fill_area == NULL)
+- fill_area = &buffer->area;
++ if (fill_area == NULL) {
++ fill_area = &buffer->logical_area;
++ logical_fill_area = buffer->logical_area;
++ } else {
++ logical_fill_area = *fill_area;
++ ply_rectangle_downscale (&logical_fill_area, scale);
++ }
+
+- ply_pixel_buffer_crop_area_to_clip_area (buffer, fill_area, &cropped_area);
++ ply_pixel_buffer_crop_area_to_clip_area (buffer, &logical_fill_area, &cropped_area);
++
++ if (clip_area) {
++ ply_rectangle_t device_clip_area;
+
+- if (clip_area)
+- ply_rectangle_intersect (&cropped_area, clip_area, &cropped_area);
++ device_clip_area = *clip_area;
++ ply_rectangle_downscale (&device_clip_area, scale);
++ ply_pixel_buffer_adjust_area_for_device_scale (buffer, &device_clip_area);
++ ply_rectangle_intersect (&cropped_area, &device_clip_area, &cropped_area);
++ }
+
+ if (cropped_area.width == 0 || cropped_area.height == 0)
+ return;
+
+- x = cropped_area.x - fill_area->x;
+- y = cropped_area.y - fill_area->y;
+ opacity_as_byte = (uint8_t) (opacity * 255.0);
++ scale_factor = (double)scale / buffer->device_scale;
++ x = cropped_area.x;
++ y = cropped_area.y;
+
++ /* column, row are the point we want to write into, in
++ pixel_buffer coordinate space (device pixels)
++
++ scale_factor * (column - fill_area->x), scale_factor * (row - fill_area->y)
++ is the point we want to source from, in the data coordinate
++ space */
+ for (row = y; row < y + cropped_area.height; row++) {
+ for (column = x; column < x + cropped_area.width; column++) {
+ uint32_t pixel_value;
+
+- pixel_value = data[fill_area->width * row + column];
++ if (buffer->device_scale == scale)
++ pixel_value = data[fill_area->width * (row - fill_area->y) +
++ column - fill_area->x];
++ else
++ pixel_value = ply_pixels_interpolate (data,
++ fill_area->width,
++ fill_area->height,
++ scale_factor * column - fill_area->x,
++ scale_factor * row - fill_area->y);
+ if ((pixel_value >> 24) == 0x00)
+ continue;
+
+ pixel_value = make_pixel_value_translucent (pixel_value, opacity_as_byte);
+ ply_pixel_buffer_blend_value_at_pixel (buffer,
+- cropped_area.x + (column - x),
+- cropped_area.y + (row - y),
++ column, row,
+ pixel_value);
+ }
+ }
+@@ -552,15 +675,30 @@ ply_pixel_buffer_fill_with_argb32_data_at_opacity_with_clip (ply_pixel_buffer_t
+ }
+
+ void
++ply_pixel_buffer_fill_with_argb32_data_at_opacity_with_clip (ply_pixel_buffer_t *buffer,
++ ply_rectangle_t *fill_area,
++ ply_rectangle_t *clip_area,
++ uint32_t *data,
++ double opacity)
++{
++ ply_pixel_buffer_fill_with_argb32_data_at_opacity_with_clip_and_scale (buffer,
++ fill_area,
++ clip_area,
++ data,
++ opacity,
++ 1);
++}
++
++void
+ ply_pixel_buffer_fill_with_argb32_data_at_opacity (ply_pixel_buffer_t *buffer,
+ ply_rectangle_t *fill_area,
+ uint32_t *data,
+ double opacity)
+ {
+- ply_pixel_buffer_fill_with_argb32_data_at_opacity_with_clip (buffer,
+- fill_area,
+- NULL,
+- data, opacity);
++ ply_pixel_buffer_fill_with_argb32_data_at_opacity_with_clip_and_scale (buffer,
++ fill_area,
++ NULL,
++ data, opacity, 1);
+ }
+
+ void
+@@ -568,10 +706,10 @@ ply_pixel_buffer_fill_with_argb32_data (ply_pixel_buffer_t *buffer,
+ ply_rectangle_t *fill_area,
+ uint32_t *data)
+ {
+- ply_pixel_buffer_fill_with_argb32_data_at_opacity_with_clip (buffer,
+- fill_area,
+- NULL,
+- data, 1.0);
++ ply_pixel_buffer_fill_with_argb32_data_at_opacity_with_clip_and_scale (buffer,
++ fill_area,
++ NULL,
++ data, 1.0, 1);
+ }
+
+ void
+@@ -580,10 +718,25 @@ ply_pixel_buffer_fill_with_argb32_data_with_clip (ply_pixel_buffer_t *buffer,
+ ply_rectangle_t *clip_area,
+ uint32_t *data)
+ {
+- ply_pixel_buffer_fill_with_argb32_data_at_opacity_with_clip (buffer,
+- fill_area,
+- clip_area,
+- data, 1.0);
++ ply_pixel_buffer_fill_with_argb32_data_at_opacity_with_clip_and_scale (buffer,
++ fill_area,
++ clip_area,
++ data, 1.0, 1);
++}
++
++static void
++ply_pixel_buffer_copy_area (ply_pixel_buffer_t *canvas,
++ ply_pixel_buffer_t *source,
++ int x, int y,
++ ply_rectangle_t *cropped_area)
++{
++ unsigned long row;
++
++ for (row = y; row < y + cropped_area->height; row++) {
++ memcpy (canvas->bytes + (cropped_area->y + row - y) * canvas->area.width + cropped_area->x,
++ source->bytes + (row * source->area.width) + x,
++ cropped_area->width * 4);
++ }
+ }
+
+ void
+@@ -594,51 +747,51 @@ ply_pixel_buffer_fill_with_buffer_at_opacity_with_clip (ply_pixel_buffer_t *canv
+ ply_rectangle_t *clip_area,
+ float opacity)
+ {
+- unsigned long row, column;
+- uint8_t opacity_as_byte;
+- ply_rectangle_t cropped_area;
++ ply_rectangle_t fill_area;
+ unsigned long x;
+ unsigned long y;
+
+ assert (canvas != NULL);
+ assert (source != NULL);
+
+- cropped_area.x = x_offset;
+- cropped_area.y = y_offset;
+- cropped_area.width = source->area.width;
+- cropped_area.height = source->area.height;
++ /* Fast path to memcpy if we need no blending or scaling */
++ if (opacity == 1.0 && ply_pixel_buffer_is_opaque (source) &&
++ canvas->device_scale == source->device_scale) {
++ ply_rectangle_t cropped_area;
+
+- ply_pixel_buffer_crop_area_to_clip_area (canvas, &cropped_area, &cropped_area);
++ cropped_area.x = x_offset;
++ cropped_area.y = y_offset;
++ cropped_area.width = source->logical_area.width;
++ cropped_area.height = source->logical_area.height;
+
+- if (clip_area)
+- ply_rectangle_intersect (&cropped_area, clip_area, &cropped_area);
++ ply_pixel_buffer_crop_area_to_clip_area (canvas, &cropped_area, &cropped_area);
+
+- if (cropped_area.width == 0 || cropped_area.height == 0)
+- return;
+-
+- x = cropped_area.x - x_offset;
+- y = cropped_area.y - y_offset;
+- opacity_as_byte = (uint8_t) (opacity * 255.0);
+-
+- for (row = y; row < y + cropped_area.height; row++) {
+- for (column = x; column < x + cropped_area.width; column++) {
+- uint32_t pixel_value;
++ /* clip_area is in source device pixels, which are also canvas device pixels */
++ if (clip_area)
++ ply_rectangle_intersect (&cropped_area, clip_area, &cropped_area);
+
+- pixel_value = source->bytes[row * source->area.width + column];
++ if (cropped_area.width == 0 || cropped_area.height == 0)
++ return;
+
+- pixel_value = make_pixel_value_translucent (pixel_value, opacity_as_byte);
++ x = cropped_area.x - x_offset;
++ y = cropped_area.y - y_offset;
+
+- if ((pixel_value >> 24) == 0x00)
+- continue;
++ ply_pixel_buffer_copy_area (canvas, source, x, y, &cropped_area);
+
+- ply_pixel_buffer_blend_value_at_pixel (canvas,
+- cropped_area.x + (column - x),
+- cropped_area.y + (row - y),
+- pixel_value);
+- }
++ ply_region_add_rectangle (canvas->updated_areas, &cropped_area);
++ } else {
++ fill_area.x = x_offset * source->device_scale;
++ fill_area.y = y_offset * source->device_scale;
++ fill_area.width = source->area.width;
++ fill_area.height = source->area.height;
++
++ ply_pixel_buffer_fill_with_argb32_data_at_opacity_with_clip_and_scale (canvas,
++ &fill_area,
++ clip_area,
++ source->bytes,
++ opacity,
++ source->device_scale);
+ }
+-
+- ply_region_add_rectangle (canvas->updated_areas, &cropped_area);
+ }
+
+ void
+@@ -696,52 +849,15 @@ ply_pixel_buffer_interpolate (ply_pixel_buffer_t *buffer,
+ double x,
+ double y)
+ {
+- int ix;
+- int iy;
+ int width;
+ int height;
+-
+- int i;
+-
+- int offset_x;
+- int offset_y;
+- uint32_t pixels[2][2];
+- uint32_t reply = 0;
+ uint32_t *bytes;
+
+ width = buffer->area.width;
+ height = buffer->area.height;
+-
+-
+ bytes = ply_pixel_buffer_get_argb32_data (buffer);
+
+- for (offset_y = 0; offset_y < 2; offset_y++) {
+- for (offset_x = 0; offset_x < 2; offset_x++) {
+- ix = x + offset_x;
+- iy = y + offset_y;
+-
+- if (ix < 0 || ix >= width || iy < 0 || iy >= height)
+- pixels[offset_y][offset_x] = 0x00000000;
+- else
+- pixels[offset_y][offset_x] = bytes[ix + iy * width];
+- }
+- }
+- if (!pixels[0][0] && !pixels[0][1] && !pixels[1][0] && !pixels[1][1]) return 0;
+-
+- ix = x;
+- iy = y;
+- x -= ix;
+- y -= iy;
+- for (i = 0; i < 4; i++) {
+- uint32_t value = 0;
+- uint32_t mask = 0xFF << (i * 8);
+- value += ((pixels[0][0]) & mask) * (1 - x) * (1 - y);
+- value += ((pixels[0][1]) & mask) * x * (1 - y);
+- value += ((pixels[1][0]) & mask) * (1 - x) * y;
+- value += ((pixels[1][1]) & mask) * x * y;
+- reply |= value & mask;
+- }
+- return reply;
++ return ply_pixels_interpolate (bytes, width, height, x, y);
+ }
+
+ ply_pixel_buffer_t *
+@@ -753,7 +869,7 @@ ply_pixel_buffer_resize (ply_pixel_buffer_t *old_buffer,
+ int x, y;
+ double old_x, old_y;
+ int old_width, old_height;
+- float scale_x, scale_y;
++ double scale_x, scale_y;
+ uint32_t *bytes;
+
+ buffer = ply_pixel_buffer_new (width, height);
+@@ -852,4 +968,20 @@ ply_pixel_buffer_tile (ply_pixel_buffer_t *old_buffer,
+ return buffer;
+ }
+
++int
++ply_pixel_buffer_get_device_scale (ply_pixel_buffer_t *buffer)
++{
++ return buffer->device_scale;
++}
++
++void
++ply_pixel_buffer_set_device_scale (ply_pixel_buffer_t *buffer,
++ int scale)
++{
++ buffer->device_scale = scale;
++
++ buffer->logical_area.width = buffer->area.width / scale;
++ buffer->logical_area.height = buffer->area.height / scale;
++}
++
+ /* vim: set ts=4 sw=4 et ai ci cino={.5s,^-2,+.5s,t0,g0,e-2,n-2,p2s,(0,=.5s,:.5s */
+diff --git a/src/libply-splash-core/ply-pixel-buffer.h b/src/libply-splash-core/ply-pixel-buffer.h
+index 7848a98..595e9bd 100644
+--- a/src/libply-splash-core/ply-pixel-buffer.h
++++ b/src/libply-splash-core/ply-pixel-buffer.h
+@@ -43,10 +43,17 @@ ply_pixel_buffer_t *ply_pixel_buffer_new (unsigned long width,
+ void ply_pixel_buffer_free (ply_pixel_buffer_t *buffer);
+ void ply_pixel_buffer_get_size (ply_pixel_buffer_t *buffer,
+ ply_rectangle_t *size);
++int ply_pixel_buffer_get_device_scale (ply_pixel_buffer_t *buffer);
++void ply_pixel_buffer_set_device_scale (ply_pixel_buffer_t *buffer,
++ int scale);
+
+ unsigned long ply_pixel_buffer_get_width (ply_pixel_buffer_t *buffer);
+ unsigned long ply_pixel_buffer_get_height (ply_pixel_buffer_t *buffer);
+
++bool ply_pixel_buffer_is_opaque (ply_pixel_buffer_t *buffer);
++void ply_pixel_buffer_set_opaque (ply_pixel_buffer_t *buffer,
++ bool is_opaque);
++
+ ply_region_t *ply_pixel_buffer_get_updated_areas (ply_pixel_buffer_t *buffer);
+
+ void ply_pixel_buffer_fill_with_color (ply_pixel_buffer_t *buffer,
+@@ -86,6 +93,12 @@ void ply_pixel_buffer_fill_with_argb32_data_at_opacity_with_clip (ply_pixel_buff
+ ply_rectangle_t *clip_area,
+ uint32_t *data,
+ double opacity);
++void ply_pixel_buffer_fill_with_argb32_data_at_opacity_with_clip_and_scale (ply_pixel_buffer_t *buffer,
++ ply_rectangle_t *fill_area,
++ ply_rectangle_t *clip_area,
++ uint32_t *data,
++ double opacity,
++ int scale);
+
+ void ply_pixel_buffer_fill_with_buffer_at_opacity_with_clip (ply_pixel_buffer_t *canvas,
+ ply_pixel_buffer_t *source,
+diff --git a/src/libply-splash-core/ply-pixel-display.c b/src/libply-splash-core/ply-pixel-display.c
+index b061bfc..cb01a2c 100644
+--- a/src/libply-splash-core/ply-pixel-display.c
++++ b/src/libply-splash-core/ply-pixel-display.c
+@@ -81,6 +81,18 @@ ply_pixel_display_new (ply_renderer_t *renderer,
+ return display;
+ }
+
++ply_renderer_t *
++ply_pixel_display_get_renderer (ply_pixel_display_t *display)
++{
++ return display->renderer;
++}
++
++ply_renderer_head_t *
++ply_pixel_display_get_renderer_head (ply_pixel_display_t *display)
++{
++ return display->head;
++}
++
+ unsigned long
+ ply_pixel_display_get_width (ply_pixel_display_t *display)
+ {
+diff --git a/src/libply-splash-core/ply-pixel-display.h b/src/libply-splash-core/ply-pixel-display.h
+index 62a36da..675c181 100644
+--- a/src/libply-splash-core/ply-pixel-display.h
++++ b/src/libply-splash-core/ply-pixel-display.h
+@@ -46,6 +46,9 @@ ply_pixel_display_t *ply_pixel_display_new (ply_renderer_t *renderer,
+
+ void ply_pixel_display_free (ply_pixel_display_t *display);
+
++ply_renderer_t *ply_pixel_display_get_renderer (ply_pixel_display_t *display);
++ply_renderer_head_t *ply_pixel_display_get_renderer_head (ply_pixel_display_t *display);
++
+ unsigned long ply_pixel_display_get_width (ply_pixel_display_t *display);
+ unsigned long ply_pixel_display_get_height (ply_pixel_display_t *display);
+
+diff --git a/src/libply-splash-core/ply-renderer-plugin.h b/src/libply-splash-core/ply-renderer-plugin.h
+index 82ef913..f1455d3 100644
+--- a/src/libply-splash-core/ply-renderer-plugin.h
++++ b/src/libply-splash-core/ply-renderer-plugin.h
+@@ -66,6 +66,8 @@ typedef struct
+
+ void (*close_input_source)(ply_renderer_backend_t *backend,
+ ply_renderer_input_source_t *input_source);
++
++ const char * (*get_device_name)(ply_renderer_backend_t *backend);
+ } ply_renderer_plugin_interface_t;
+
+ #endif /* PLY_RENDERER_PLUGIN_H */
+diff --git a/src/libply-splash-core/ply-renderer.c b/src/libply-splash-core/ply-renderer.c
+index f64eac5..b9059ef 100644
+--- a/src/libply-splash-core/ply-renderer.c
++++ b/src/libply-splash-core/ply-renderer.c
+@@ -55,6 +55,7 @@ struct _ply_renderer
+
+ uint32_t input_source_is_open : 1;
+ uint32_t is_mapped : 1;
++ uint32_t is_active : 1;
+ };
+
+ typedef const ply_renderer_plugin_interface_t *
+@@ -154,6 +155,11 @@ ply_renderer_load_plugin (ply_renderer_t *renderer,
+ return false;
+ }
+
++ if (renderer->plugin_interface->get_device_name != NULL) {
++ free (renderer->device_name);
++ renderer->device_name = strdup (renderer->plugin_interface->get_device_name (renderer->backend));
++ }
++
+ return true;
+ }
+
+@@ -268,15 +274,19 @@ ply_renderer_open (ply_renderer_t *renderer)
+ { PLY_RENDERER_TYPE_NONE, NULL }
+ };
+
++ renderer->is_active = false;
+ for (i = 0; known_plugins[i].type != PLY_RENDERER_TYPE_NONE; i++) {
+ if (renderer->type == known_plugins[i].type ||
+ renderer->type == PLY_RENDERER_TYPE_AUTO)
+- if (ply_renderer_open_plugin (renderer, known_plugins[i].path))
+- return true;
++ if (ply_renderer_open_plugin (renderer, known_plugins[i].path)) {
++ renderer->is_active = true;
++ goto out;
++ }
+ }
+
+ ply_trace ("could not find suitable rendering plugin");
+- return false;
++out:
++ return renderer->is_active;
+ }
+
+ void
+@@ -284,6 +294,7 @@ ply_renderer_close (ply_renderer_t *renderer)
+ {
+ ply_renderer_unmap_from_device (renderer);
+ ply_renderer_close_device (renderer);
++ renderer->is_active = false;
+ }
+
+ void
+@@ -291,7 +302,11 @@ ply_renderer_activate (ply_renderer_t *renderer)
+ {
+ assert (renderer->plugin_interface != NULL);
+
+- return renderer->plugin_interface->activate (renderer->backend);
++ if (renderer->is_active)
++ return;
++
++ renderer->plugin_interface->activate (renderer->backend);
++ renderer->is_active = true;
+ }
+
+ void
+@@ -302,6 +317,12 @@ ply_renderer_deactivate (ply_renderer_t *renderer)
+ return renderer->plugin_interface->deactivate (renderer->backend);
+ }
+
++bool
++ply_renderer_is_active (ply_renderer_t *renderer)
++{
++ return renderer->is_active;
++}
++
+ ply_list_t *
+ ply_renderer_get_heads (ply_renderer_t *renderer)
+ {
+diff --git a/src/libply-splash-core/ply-renderer.h b/src/libply-splash-core/ply-renderer.h
+index a019a4d..59391e1 100644
+--- a/src/libply-splash-core/ply-renderer.h
++++ b/src/libply-splash-core/ply-renderer.h
+@@ -57,6 +57,7 @@ bool ply_renderer_open (ply_renderer_t *renderer);
+ void ply_renderer_close (ply_renderer_t *renderer);
+ void ply_renderer_activate (ply_renderer_t *renderer);
+ void ply_renderer_deactivate (ply_renderer_t *renderer);
++bool ply_renderer_is_active (ply_renderer_t *renderer);
+ const char *ply_renderer_get_device_name (ply_renderer_t *renderer);
+ ply_list_t *ply_renderer_get_heads (ply_renderer_t *renderer);
+ ply_pixel_buffer_t *ply_renderer_get_buffer_for_head (ply_renderer_t *renderer,
+diff --git a/src/libply-splash-core/ply-seat.c b/src/libply-splash-core/ply-seat.c
+deleted file mode 100644
+index 700332f..0000000
+--- a/src/libply-splash-core/ply-seat.c
++++ /dev/null
+@@ -1,381 +0,0 @@
+-/* ply-seat.c - APIs for encapsulating a keyboard and one or more displays
+- *
+- * Copyright (C) 2013 Red Hat, Inc.
+- *
+- * This program is free software; you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License as published by
+- * the Free Software Foundation; either version 2, or (at your option)
+- * any later version.
+- *
+- * This program is distributed in the hope that it will be useful,
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- * GNU General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with this program; if not, write to the Free Software
+- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+- * 02111-1307, USA.
+- *
+- * Written by: Ray Strode <rstrode@redhat.com>
+- */
+-#include "config.h"
+-#include "ply-seat.h"
+-
+-#include <assert.h>
+-#include <errno.h>
+-#include <stdint.h>
+-#include <stdio.h>
+-#include <stdlib.h>
+-#include <unistd.h>
+-
+-#include "ply-boot-splash.h"
+-#include "ply-event-loop.h"
+-#include "ply-keyboard.h"
+-#include "ply-pixel-display.h"
+-#include "ply-text-display.h"
+-#include "ply-list.h"
+-#include "ply-logger.h"
+-#include "ply-utils.h"
+-
+-struct _ply_seat
+-{
+- ply_event_loop_t *loop;
+-
+- ply_boot_splash_t *splash;
+- ply_terminal_t *terminal;
+- ply_renderer_t *renderer;
+- ply_keyboard_t *keyboard;
+- ply_list_t *text_displays;
+- ply_list_t *pixel_displays;
+-
+- uint32_t renderer_active : 1;
+- uint32_t keyboard_active : 1;
+-};
+-
+-ply_seat_t *
+-ply_seat_new (ply_terminal_t *terminal)
+-{
+- ply_seat_t *seat;
+-
+- seat = calloc (1, sizeof(ply_seat_t));
+-
+- seat->loop = ply_event_loop_get_default ();
+- seat->terminal = terminal;
+- seat->text_displays = ply_list_new ();
+- seat->pixel_displays = ply_list_new ();
+-
+- return seat;
+-}
+-
+-static void
+-add_pixel_displays (ply_seat_t *seat)
+-{
+- ply_list_t *heads;
+- ply_list_node_t *node;
+-
+- heads = ply_renderer_get_heads (seat->renderer);
+-
+- ply_trace ("Adding displays for %d heads",
+- ply_list_get_length (heads));
+-
+- node = ply_list_get_first_node (heads);
+- while (node != NULL) {
+- ply_list_node_t *next_node;
+- ply_renderer_head_t *head;
+- ply_pixel_display_t *display;
+-
+- head = ply_list_node_get_data (node);
+- next_node = ply_list_get_next_node (heads, node);
+-
+- display = ply_pixel_display_new (seat->renderer, head);
+-
+- ply_list_append_data (seat->pixel_displays, display);
+-
+- node = next_node;
+- }
+-}
+-
+-static void
+-add_text_displays (ply_seat_t *seat)
+-{
+- ply_text_display_t *display;
+-
+- if (!ply_terminal_is_open (seat->terminal)) {
+- if (!ply_terminal_open (seat->terminal)) {
+- ply_trace ("could not add terminal %s: %m",
+- ply_terminal_get_name (seat->terminal));
+- return;
+- }
+- }
+-
+- ply_trace ("adding text display for terminal %s",
+- ply_terminal_get_name (seat->terminal));
+-
+- display = ply_text_display_new (seat->terminal);
+- ply_list_append_data (seat->text_displays, display);
+-}
+-
+-bool
+-ply_seat_open (ply_seat_t *seat,
+- ply_renderer_type_t renderer_type,
+- const char *device)
+-{
+- if (renderer_type != PLY_RENDERER_TYPE_NONE) {
+- ply_renderer_t *renderer;
+-
+- renderer = ply_renderer_new (renderer_type, device, seat->terminal);
+-
+- if (!ply_renderer_open (renderer)) {
+- ply_trace ("could not open renderer for %s", device);
+- ply_renderer_free (renderer);
+-
+- seat->renderer = NULL;
+- seat->renderer_active = false;
+-
+- if (renderer_type != PLY_RENDERER_TYPE_AUTO)
+- return false;
+- } else {
+- seat->renderer = renderer;
+- seat->renderer_active = true;
+- }
+- }
+-
+- if (seat->renderer != NULL) {
+- seat->keyboard = ply_keyboard_new_for_renderer (seat->renderer);
+- add_pixel_displays (seat);
+- } else if (seat->terminal != NULL) {
+- seat->keyboard = ply_keyboard_new_for_terminal (seat->terminal);
+- }
+-
+- if (seat->terminal != NULL) {
+- add_text_displays (seat);
+- } else {
+- ply_trace ("not adding text display for seat, since seat has no associated terminal");
+- }
+-
+- if (seat->keyboard != NULL) {
+- ply_keyboard_watch_for_input (seat->keyboard);
+- seat->keyboard_active = true;
+- } else {
+- ply_trace ("not watching seat for input");
+- }
+-
+- return true;
+-}
+-
+-bool
+-ply_seat_is_open (ply_seat_t *seat)
+-{
+- return ply_list_get_length (seat->pixel_displays) > 0 ||
+- ply_list_get_length (seat->text_displays) > 0;
+-}
+-
+-void
+-ply_seat_deactivate_keyboard (ply_seat_t *seat)
+-{
+- if (!seat->keyboard_active)
+- return;
+-
+- seat->keyboard_active = false;
+-
+- if (seat->keyboard == NULL)
+- return;
+-
+- ply_trace ("deactivating keyboard");
+- ply_keyboard_stop_watching_for_input (seat->keyboard);
+-}
+-
+-void
+-ply_seat_deactivate_renderer (ply_seat_t *seat)
+-{
+- if (!seat->renderer_active)
+- return;
+-
+- seat->renderer_active = false;
+-
+- if (seat->renderer == NULL)
+- return;
+-
+- ply_trace ("deactivating renderer");
+- ply_renderer_deactivate (seat->renderer);
+-}
+-
+-void
+-ply_seat_activate_keyboard (ply_seat_t *seat)
+-{
+- if (seat->keyboard_active)
+- return;
+-
+- if (seat->keyboard == NULL)
+- return;
+-
+- ply_trace ("activating keyboard");
+- ply_keyboard_watch_for_input (seat->keyboard);
+-
+- seat->keyboard_active = true;
+-}
+-
+-void
+-ply_seat_activate_renderer (ply_seat_t *seat)
+-{
+- if (seat->renderer_active)
+- return;
+-
+- if (seat->renderer == NULL)
+- return;
+-
+- ply_trace ("activating renderer");
+- ply_renderer_activate (seat->renderer);
+-
+- seat->renderer_active = true;
+-}
+-
+-void
+-ply_seat_refresh_displays (ply_seat_t *seat)
+-{
+- ply_list_node_t *node;
+-
+- node = ply_list_get_first_node (seat->pixel_displays);
+- while (node != NULL) {
+- ply_pixel_display_t *display;
+- ply_list_node_t *next_node;
+- unsigned long width, height;
+-
+- display = ply_list_node_get_data (node);
+- next_node = ply_list_get_next_node (seat->pixel_displays, node);
+-
+- width = ply_pixel_display_get_width (display);
+- height = ply_pixel_display_get_height (display);
+-
+- ply_pixel_display_draw_area (display, 0, 0, width, height);
+- node = next_node;
+- }
+-
+- node = ply_list_get_first_node (seat->text_displays);
+- while (node != NULL) {
+- ply_text_display_t *display;
+- ply_list_node_t *next_node;
+- int number_of_columns, number_of_rows;
+-
+- display = ply_list_node_get_data (node);
+- next_node = ply_list_get_next_node (seat->text_displays, node);
+-
+- number_of_columns = ply_text_display_get_number_of_columns (display);
+- number_of_rows = ply_text_display_get_number_of_rows (display);
+-
+- ply_text_display_draw_area (display, 0, 0,
+- number_of_columns,
+- number_of_rows);
+- node = next_node;
+- }
+-}
+-
+-void
+-ply_seat_close (ply_seat_t *seat)
+-{
+- if (seat->renderer == NULL)
+- return;
+-
+- ply_trace ("destroying renderer");
+- ply_renderer_close (seat->renderer);
+- ply_renderer_free (seat->renderer);
+- seat->renderer = NULL;
+-}
+-
+-void
+-ply_seat_set_splash (ply_seat_t *seat,
+- ply_boot_splash_t *splash)
+-{
+- if (seat->splash == splash)
+- return;
+-
+- if (seat->splash != NULL)
+- ply_boot_splash_detach_from_seat (splash, seat);
+-
+- if (splash != NULL)
+- ply_boot_splash_attach_to_seat (splash, seat);
+-
+- seat->splash = splash;
+-}
+-
+-static void
+-free_pixel_displays (ply_seat_t *seat)
+-{
+- ply_list_node_t *node;
+-
+- ply_trace ("freeing %d pixel displays", ply_list_get_length (seat->pixel_displays));
+- node = ply_list_get_first_node (seat->pixel_displays);
+- while (node != NULL) {
+- ply_list_node_t *next_node;
+- ply_pixel_display_t *display;
+-
+- next_node = ply_list_get_next_node (seat->pixel_displays, node);
+- display = ply_list_node_get_data (node);
+- ply_pixel_display_free (display);
+-
+- ply_list_remove_node (seat->pixel_displays, node);
+-
+- node = next_node;
+- }
+-}
+-
+-static void
+-free_text_displays (ply_seat_t *seat)
+-{
+- ply_list_node_t *node;
+-
+- ply_trace ("freeing %d text displays", ply_list_get_length (seat->text_displays));
+- node = ply_list_get_first_node (seat->text_displays);
+- while (node != NULL) {
+- ply_list_node_t *next_node;
+- ply_text_display_t *display;
+-
+- next_node = ply_list_get_next_node (seat->text_displays, node);
+- display = ply_list_node_get_data (node);
+- ply_text_display_free (display);
+-
+- ply_list_remove_node (seat->text_displays, node);
+-
+- node = next_node;
+- }
+-}
+-
+-void
+-ply_seat_free (ply_seat_t *seat)
+-{
+- if (seat == NULL)
+- return;
+-
+- free_pixel_displays (seat);
+- free_text_displays (seat);
+- ply_keyboard_free (seat->keyboard);
+-
+- free (seat);
+-}
+-
+-ply_list_t *
+-ply_seat_get_pixel_displays (ply_seat_t *seat)
+-{
+- return seat->pixel_displays;
+-}
+-
+-ply_list_t *
+-ply_seat_get_text_displays (ply_seat_t *seat)
+-{
+- return seat->text_displays;
+-}
+-
+-ply_keyboard_t *
+-ply_seat_get_keyboard (ply_seat_t *seat)
+-{
+- return seat->keyboard;
+-}
+-
+-ply_renderer_t *
+-ply_seat_get_renderer (ply_seat_t *seat)
+-{
+- return seat->renderer;
+-}
+-
+-/* vim: set ts=4 sw=4 et ai ci cino={.5s,^-2,+.5s,t0,g0,e-2,n-2,p2s,(0,=.5s,:.5s */
+diff --git a/src/libply-splash-core/ply-seat.h b/src/libply-splash-core/ply-seat.h
+deleted file mode 100644
+index f9ed15d..0000000
+--- a/src/libply-splash-core/ply-seat.h
++++ /dev/null
+@@ -1,66 +0,0 @@
+-/* ply-seat.h - APIs for encapsulating a keyboard and one or more displays
+- *
+- * Copyright (C) 2013 Red Hat, Inc.
+- *
+- * This program is free software; you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License as published by
+- * the Free Software Foundation; either version 2, or (at your option)
+- * any later version.
+- *
+- * This program is distributed in the hope that it will be useful,
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- * GNU General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with this program; if not, write to the Free Software
+- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+- * 02111-1307, USA.
+- *
+- * Written By: Ray Strode <rstrode@redhat.com>
+- */
+-#ifndef PLY_SEAT_H
+-#define PLY_SEAT_H
+-
+-#include <stdarg.h>
+-#include <stdbool.h>
+-#include <stdint.h>
+-#include <unistd.h>
+-
+-#include "ply-boot-splash.h"
+-#include "ply-buffer.h"
+-#include "ply-event-loop.h"
+-#include "ply-keyboard.h"
+-#include "ply-list.h"
+-#include "ply-pixel-display.h"
+-#include "ply-terminal.h"
+-#include "ply-text-display.h"
+-
+-typedef struct _ply_boot_splash ply_boot_splash_t;
+-typedef struct _ply_seat ply_seat_t;
+-
+-#ifndef PLY_HIDE_FUNCTION_DECLARATIONS
+-ply_seat_t *ply_seat_new (ply_terminal_t *terminal);
+-
+-void ply_seat_free (ply_seat_t *seat);
+-bool ply_seat_open (ply_seat_t *seat,
+- ply_renderer_type_t renderer_type,
+- const char *device);
+-bool ply_seat_is_open (ply_seat_t *seat);
+-void ply_seat_deactivate_keyboard (ply_seat_t *seat);
+-void ply_seat_activate_keyboard (ply_seat_t *seat);
+-void ply_seat_deactivate_renderer (ply_seat_t *seat);
+-void ply_seat_activate_renderer (ply_seat_t *seat);
+-void ply_seat_refresh_displays (ply_seat_t *seat);
+-void ply_seat_close (ply_seat_t *seat);
+-void ply_seat_set_splash (ply_seat_t *seat,
+- ply_boot_splash_t *splash);
+-
+-ply_list_t *ply_seat_get_pixel_displays (ply_seat_t *seat);
+-ply_list_t *ply_seat_get_text_displays (ply_seat_t *seat);
+-ply_keyboard_t *ply_seat_get_keyboard (ply_seat_t *seat);
+-ply_renderer_t *ply_seat_get_renderer (ply_seat_t *seat);
+-#endif
+-
+-#endif /* PLY_SEAT_H */
+-/* vim: set ts=4 sw=4 et ai ci cino={.5s,^-2,+.5s,t0,g0,e-2,n-2,p2s,(0,=.5s,:.5s */
+diff --git a/src/libply-splash-core/ply-terminal.c b/src/libply-splash-core/ply-terminal.c
+index 1bbeb42..a0954f2 100644
+--- a/src/libply-splash-core/ply-terminal.c
++++ b/src/libply-splash-core/ply-terminal.c
+@@ -387,8 +387,8 @@ on_tty_disconnected (ply_terminal_t *terminal)
+ ply_terminal_reopen_device (terminal);
+ }
+
+-static bool
+-ply_terminal_look_up_geometry (ply_terminal_t *terminal)
++bool
++ply_terminal_refresh_geometry (ply_terminal_t *terminal)
+ {
+ struct winsize terminal_size;
+
+@@ -551,7 +551,7 @@ ply_terminal_open_device (ply_terminal_t *terminal)
+ assert (terminal->fd < 0);
+ assert (terminal->fd_watch == NULL);
+
+- terminal->fd = open (terminal->name, O_RDWR | O_NOCTTY);
++ terminal->fd = open (terminal->name, O_RDWR | O_NOCTTY | O_NONBLOCK);
+
+ if (terminal->fd < 0) {
+ ply_trace ("Unable to open terminal device '%s': %m", terminal->name);
+@@ -570,6 +570,8 @@ ply_terminal_open_device (ply_terminal_t *terminal)
+ return PLY_TERMINAL_OPEN_RESULT_FAILURE;
+ }
+
++ ply_set_fd_as_blocking (terminal->fd);
++
+ terminal->fd_watch = ply_event_loop_watch_fd (terminal->loop, terminal->fd,
+ PLY_EVENT_LOOP_FD_STATUS_HAS_DATA,
+ (ply_event_handler_t) on_tty_input,
+@@ -605,7 +607,7 @@ ply_terminal_open (ply_terminal_t *terminal)
+ return false;
+ }
+
+- ply_terminal_look_up_geometry (terminal);
++ ply_terminal_refresh_geometry (terminal);
+
+ ply_terminal_look_up_color_palette (terminal);
+ ply_terminal_save_color_palette (terminal);
+@@ -613,7 +615,7 @@ ply_terminal_open (ply_terminal_t *terminal)
+ ply_event_loop_watch_signal (terminal->loop,
+ SIGWINCH,
+ (ply_event_handler_t)
+- ply_terminal_look_up_geometry,
++ ply_terminal_refresh_geometry,
+ terminal);
+
+ if (ply_terminal_is_vt (terminal)) {
+diff --git a/src/libply-splash-core/ply-terminal.h b/src/libply-splash-core/ply-terminal.h
+index dc83ec3..7cfcc59 100644
+--- a/src/libply-splash-core/ply-terminal.h
++++ b/src/libply-splash-core/ply-terminal.h
+@@ -69,6 +69,7 @@ void ply_terminal_reset_colors (ply_terminal_t *terminal);
+
+ bool ply_terminal_set_unbuffered_input (ply_terminal_t *terminal);
+ bool ply_terminal_set_buffered_input (ply_terminal_t *terminal);
++bool ply_terminal_refresh_geometry (ply_terminal_t *terminal);
+
+ __attribute__((__format__ (__printf__, 2, 3)))
+ void ply_terminal_write (ply_terminal_t *terminal,
+diff --git a/src/libply-splash-graphics/ply-animation.c b/src/libply-splash-graphics/ply-animation.c
+index 81348f9..323d9ed 100644
+--- a/src/libply-splash-graphics/ply-animation.c
++++ b/src/libply-splash-graphics/ply-animation.c
+@@ -353,6 +353,11 @@ ply_animation_stop_now (ply_animation_t *animation)
+ void
+ ply_animation_stop (ply_animation_t *animation)
+ {
++ if (animation->is_stopped) {
++ ply_trace ("animation already stopped, ignoring stop request");
++ return;
++ }
++
+ if (animation->stop_trigger == NULL) {
+ ply_animation_stop_now (animation);
+ return;
+diff --git a/src/libply-splash-graphics/ply-image.c b/src/libply-splash-graphics/ply-image.c
+index f838601..8b46978 100644
+--- a/src/libply-splash-graphics/ply-image.c
++++ b/src/libply-splash-graphics/ply-image.c
+@@ -99,10 +99,13 @@ transform_to_argb32 (png_struct *png,
+ blue = data[i + 2];
+ alpha = data[i + 3];
+
+- red = (uint8_t) CLAMP (((red / 255.0) * (alpha / 255.0)) * 255.0, 0, 255.0);
+- green = (uint8_t) CLAMP (((green / 255.0) * (alpha / 255.0)) * 255.0,
+- 0, 255.0);
+- blue = (uint8_t) CLAMP (((blue / 255.0) * (alpha / 255.0)) * 255.0, 0, 255.0);
++ /* pre-multiply the alpha if there's translucency */
++ if (alpha != 0xff) {
++ red = (uint8_t) CLAMP (((red / 255.0) * (alpha / 255.0)) * 255.0, 0, 255.0);
++ green = (uint8_t) CLAMP (((green / 255.0) * (alpha / 255.0)) * 255.0,
++ 0, 255.0);
++ blue = (uint8_t) CLAMP (((blue / 255.0) * (alpha / 255.0)) * 255.0, 0, 255.0);
++ }
+
+ pixel_value = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0);
+ memcpy (data + i, &pixel_value, sizeof(uint32_t));
+diff --git a/src/libply-splash-graphics/ply-throbber.c b/src/libply-splash-graphics/ply-throbber.c
+index c9c5bbd..f18feb6 100644
+--- a/src/libply-splash-graphics/ply-throbber.c
++++ b/src/libply-splash-graphics/ply-throbber.c
+@@ -337,6 +337,15 @@ void
+ ply_throbber_stop (ply_throbber_t *throbber,
+ ply_trigger_t *stop_trigger)
+ {
++ if (throbber->is_stopped) {
++ ply_trace ("throbber already stopped");
++ if (stop_trigger != NULL) {
++ ply_trace ("pulling stop trigger right away");
++ ply_trigger_pull (stop_trigger, NULL);
++ }
++ return;
++ }
++
+ if (stop_trigger == NULL) {
+ ply_throbber_stop_now (throbber);
+ return;
+diff --git a/src/libply/ply-logger.h b/src/libply/ply-logger.h
+index de23181..545e64c 100644
+--- a/src/libply/ply-logger.h
++++ b/src/libply/ply-logger.h
+@@ -92,7 +92,7 @@ bool ply_logger_is_tracing_enabled (ply_logger_t *logger);
+ ply_logger_flush (logger); \
+ errno = _old_errno; \
+ ply_logger_inject (logger, \
+- "[%s:%d] %45.45s:" format "\r\n", \
++ "[%s:%d] %45.45s:" format "\n", \
+ __FILE__, __LINE__, __func__, ## args); \
+ ply_logger_flush (logger); \
+ errno = _old_errno; \
+diff --git a/src/libply/ply-utils.c b/src/libply/ply-utils.c
+index 5c8510c..89e37e9 100644
+--- a/src/libply/ply-utils.c
++++ b/src/libply/ply-utils.c
+@@ -78,6 +78,8 @@
+ static int errno_stack[PLY_ERRNO_STACK_SIZE];
+ static int errno_stack_position = 0;
+
++static int overridden_device_scale = 0;
++
+ bool
+ ply_open_unidirectional_pipe (int *sender_fd,
+ int *receiver_fd)
+@@ -421,6 +423,29 @@ ply_fd_may_block (int fd)
+ return (flags & O_NONBLOCK) != 0;
+ }
+
++bool
++ply_set_fd_as_blocking (int fd)
++{
++ int flags;
++ int ret = 0;
++
++ assert (fd >= 0);
++
++ flags = fcntl (fd, F_GETFL);
++
++ if (flags == -1) {
++ return false;
++ }
++
++ if (flags & O_NONBLOCK) {
++ flags &= ~O_NONBLOCK;
++
++ ret = fcntl (fd, F_SETFL, flags);
++ }
++
++ return ret == 0;
++}
++
+ char **
+ ply_copy_string_array (const char *const *array)
+ {
+@@ -936,4 +961,58 @@ out:
+
+ return (pid_t) ppid;
+ }
++
++void
++ply_set_device_scale (int device_scale)
++{
++ overridden_device_scale = device_scale;
++ ply_trace ("Device scale is set to %d", device_scale);
++}
++
++/* The minimum resolution at which we turn on a device-scale of 2 */
++#define HIDPI_LIMIT 192
++#define HIDPI_MIN_HEIGHT 1200
++
++int
++ply_get_device_scale (uint32_t width,
++ uint32_t height,
++ uint32_t width_mm,
++ uint32_t height_mm)
++{
++ int device_scale;
++ double dpi_x, dpi_y;
++ const char *force_device_scale;
++
++ device_scale = 1;
++
++ if ((force_device_scale = getenv ("PLYMOUTH_FORCE_SCALE")))
++ return strtoul (force_device_scale, NULL, 0);
++
++ if (overridden_device_scale != 0)
++ return overridden_device_scale;
++
++ if (height < HIDPI_MIN_HEIGHT)
++ return 1;
++
++ /* Somebody encoded the aspect ratio (16/9 or 16/10)
++ * instead of the physical size */
++ if ((width_mm == 160 && height_mm == 90) ||
++ (width_mm == 160 && height_mm == 100) ||
++ (width_mm == 16 && height_mm == 9) ||
++ (width_mm == 16 && height_mm == 10))
++ return 1;
++
++ if (width_mm > 0 && height_mm > 0) {
++ dpi_x = (double)width / (width_mm / 25.4);
++ dpi_y = (double)height / (height_mm / 25.4);
++ /* We don't completely trust these values so both
++ must be high, and never pick higher ratio than
++ 2 automatically */
++ if (dpi_x > HIDPI_LIMIT && dpi_y > HIDPI_LIMIT)
++ device_scale = 2;
++ }
++
++ return device_scale;
++}
++
+ /* vim: set ts=4 sw=4 expandtab autoindent cindent cino={.5s,(0: */
+diff --git a/src/libply/ply-utils.h b/src/libply/ply-utils.h
+index b9a28d0..c46603e 100644
+--- a/src/libply/ply-utils.h
++++ b/src/libply/ply-utils.h
+@@ -82,6 +82,7 @@ bool ply_read_uint32 (int fd,
+ bool ply_fd_has_data (int fd);
+ bool ply_fd_can_take_data (int fd);
+ bool ply_fd_may_block (int fd);
++bool ply_set_fd_as_blocking (int fd);
+ char **ply_copy_string_array (const char *const *array);
+ void ply_free_string_array (char **array);
+ bool ply_string_has_prefix (const char *string,
+@@ -120,6 +121,13 @@ int ply_utf8_string_get_length (const char *string,
+ char *ply_get_process_command_line (pid_t pid);
+ pid_t ply_get_process_parent_pid (pid_t pid);
+
++void ply_set_device_scale (int device_scale);
++
++int ply_get_device_scale (uint32_t width,
++ uint32_t height,
++ uint32_t width_mm,
++ uint32_t height_mm);
++
+ #endif
+
+ #endif /* PLY_UTILS_H */
+diff --git a/src/main.c b/src/main.c
+index da6e95d..0864e5e 100644
+--- a/src/main.c
++++ b/src/main.c
+@@ -107,6 +107,7 @@ typedef struct
+
+ double start_time;
+ double splash_delay;
++ double device_timeout;
+
+ char kernel_command_line[PLY_MAX_COMMAND_LINE_SIZE];
+ uint32_t kernel_command_line_is_set : 1;
+@@ -136,8 +137,8 @@ static ply_boot_splash_t *load_theme (state_t *state,
+ static ply_boot_splash_t *show_theme (state_t *state,
+ const char *theme_path);
+
+-static void attach_splash_to_seats (state_t *state,
+- ply_boot_splash_t *splash);
++static void attach_splash_to_devices (state_t *state,
++ ply_boot_splash_t *splash);
+ static bool attach_to_running_session (state_t *state);
+ static void detach_from_running_session (state_t *state);
+ static void on_escape_pressed (state_t *state);
+@@ -293,8 +294,8 @@ load_settings (state_t *state,
+ char **theme_path)
+ {
+ ply_key_file_t *key_file = NULL;
+- const char *delay_string;
+ bool settings_loaded = false;
++ const char *scale_string;
+ const char *splash_string;
+
+ ply_trace ("Trying to load %s", path);
+@@ -313,6 +314,8 @@ load_settings (state_t *state,
+ splash_string, splash_string);
+
+ if (isnan (state->splash_delay)) {
++ const char *delay_string;
++
+ delay_string = ply_key_file_get_value (key_file, "Daemon", "ShowDelay");
+
+ if (delay_string != NULL) {
+@@ -321,6 +324,23 @@ load_settings (state_t *state,
+ }
+ }
+
++ if (isnan (state->device_timeout)) {
++ const char *timeout_string;
++
++ timeout_string = ply_key_file_get_value (key_file, "Daemon", "DeviceTimeout");
++
++ if (timeout_string != NULL) {
++ state->device_timeout = atof (timeout_string);
++ ply_trace ("Device timeout is set to %lf", state->device_timeout);
++ }
++ }
++
++ scale_string = ply_key_file_get_value (key_file, "Daemon", "DeviceScale");
++
++ if (scale_string != NULL) {
++ ply_set_device_scale (strtoul (scale_string, NULL, 0));
++ }
++
+ settings_loaded = true;
+ out:
+ ply_key_file_free (key_file);
+@@ -345,6 +365,8 @@ show_detailed_splash (state_t *state)
+ }
+
+ state->boot_splash = splash;
++
++ show_messages (state);
+ update_display (state);
+ }
+
+@@ -419,6 +441,17 @@ find_override_splash (state_t *state)
+ }
+
+ static void
++find_force_scale (state_t *state)
++{
++ const char *scale_string;
++
++ scale_string = command_line_get_string_after_prefix (state->kernel_command_line, "plymouth.force-scale=");
++
++ if (scale_string != NULL)
++ ply_set_device_scale (strtoul (scale_string, NULL, 0));
++}
++
++static void
+ find_system_default_splash (state_t *state)
+ {
+ if (state->system_default_splash_path != NULL)
+@@ -492,6 +525,7 @@ show_default_splash (state_t *state)
+ return;
+ }
+
++ show_messages (state);
+ update_display (state);
+ }
+
+@@ -520,14 +554,14 @@ on_ask_for_password (state_t *state,
+ * arrive shortly so just sit tight
+ */
+ if (state->is_shown) {
+- bool has_open_seats;
++ bool has_displays;
+
+ cancel_pending_delayed_show (state);
+
+- has_open_seats = ply_device_manager_has_open_seats (state->device_manager);
++ has_displays = ply_device_manager_has_displays (state->device_manager);
+
+- if (has_open_seats) {
+- ply_trace ("seats open now, showing splash immediately");
++ if (has_displays) {
++ ply_trace ("displays available now, showing splash immediately");
+ show_splash (state);
+ } else {
+ ply_trace ("splash still coming up, waiting a bit");
+@@ -891,7 +925,7 @@ plymouth_should_show_default_splash (state_t *state)
+ static void
+ on_show_splash (state_t *state)
+ {
+- bool has_open_seats;
++ bool has_displays;
+
+ if (state->is_shown) {
+ ply_trace ("show splash called while already shown");
+@@ -910,49 +944,20 @@ on_show_splash (state_t *state)
+ }
+
+ state->is_shown = true;
+- has_open_seats = ply_device_manager_has_open_seats (state->device_manager);
++ has_displays = ply_device_manager_has_displays (state->device_manager);
+
+- if (!state->is_attached && state->should_be_attached && has_open_seats)
++ if (!state->is_attached && state->should_be_attached && has_displays)
+ attach_to_running_session (state);
+
+- if (has_open_seats) {
+- ply_trace ("at least one seat already open, so loading splash");
++ if (has_displays) {
++ ply_trace ("at least one display already available, so loading splash");
+ show_splash (state);
+ } else {
+- ply_trace ("no seats available to show splash on, waiting...");
++ ply_trace ("no displays available to show splash on, waiting...");
+ }
+ }
+
+ static void
+-on_seat_removed (state_t *state,
+- ply_seat_t *seat)
+-{
+- ply_keyboard_t *keyboard;
+-
+- keyboard = ply_seat_get_keyboard (seat);
+-
+- ply_trace ("no longer listening for keystrokes");
+- ply_keyboard_remove_input_handler (keyboard,
+- (ply_keyboard_input_handler_t)
+- on_keyboard_input);
+- ply_trace ("no longer listening for escape");
+- ply_keyboard_remove_escape_handler (keyboard,
+- (ply_keyboard_escape_handler_t)
+- on_escape_pressed);
+- ply_trace ("no longer listening for backspace");
+- ply_keyboard_remove_backspace_handler (keyboard,
+- (ply_keyboard_backspace_handler_t)
+- on_backspace);
+- ply_trace ("no longer listening for enter");
+- ply_keyboard_remove_enter_handler (keyboard,
+- (ply_keyboard_enter_handler_t)
+- on_enter);
+-
+- if (state->boot_splash != NULL)
+- ply_boot_splash_detach_from_seat (state->boot_splash, seat);
+-}
+-
+-static void
+ show_splash (state_t *state)
+ {
+ if (state->boot_splash != NULL)
+@@ -991,23 +996,9 @@ show_splash (state_t *state)
+ }
+
+ static void
+-on_seat_added (state_t *state,
+- ply_seat_t *seat)
++on_keyboard_added (state_t *state,
++ ply_keyboard_t *keyboard)
+ {
+- ply_keyboard_t *keyboard;
+-
+- if (state->is_shown && !state->is_inactive) {
+- if (state->boot_splash == NULL) {
+- ply_trace ("seat added before splash loaded, so loading splash now");
+- show_splash (state);
+- } else {
+- ply_trace ("seat added after splash loaded, so attaching to splash");
+- ply_boot_splash_attach_to_seat (state->boot_splash, seat);
+- }
+- }
+-
+- keyboard = ply_seat_get_keyboard (seat);
+-
+ ply_trace ("listening for keystrokes");
+ ply_keyboard_add_input_handler (keyboard,
+ (ply_keyboard_input_handler_t)
+@@ -1024,6 +1015,90 @@ on_seat_added (state_t *state,
+ ply_keyboard_add_enter_handler (keyboard,
+ (ply_keyboard_enter_handler_t)
+ on_enter, state);
++
++ if (state->boot_splash != NULL) {
++ ply_trace ("keyboard set after splash loaded, so attaching to splash");
++ ply_boot_splash_set_keyboard (state->boot_splash, keyboard);
++ }
++}
++
++static void
++on_keyboard_removed (state_t *state,
++ ply_keyboard_t *keyboard)
++{
++ ply_trace ("no longer listening for keystrokes");
++ ply_keyboard_remove_input_handler (keyboard,
++ (ply_keyboard_input_handler_t)
++ on_keyboard_input);
++ ply_trace ("no longer listening for escape");
++ ply_keyboard_remove_escape_handler (keyboard,
++ (ply_keyboard_escape_handler_t)
++ on_escape_pressed);
++ ply_trace ("no longer listening for backspace");
++ ply_keyboard_remove_backspace_handler (keyboard,
++ (ply_keyboard_backspace_handler_t)
++ on_backspace);
++ ply_trace ("no longer listening for enter");
++ ply_keyboard_remove_enter_handler (keyboard,
++ (ply_keyboard_enter_handler_t)
++ on_enter);
++
++ if (state->boot_splash != NULL)
++ ply_boot_splash_unset_keyboard (state->boot_splash);
++}
++
++static void
++on_pixel_display_added (state_t *state,
++ ply_pixel_display_t *display)
++{
++ if (state->is_shown) {
++ if (state->boot_splash == NULL) {
++ ply_trace ("pixel display added before splash loaded, so loading splash now");
++ show_splash (state);
++ } else {
++ ply_trace ("pixel display added after splash loaded, so attaching to splash");
++ ply_boot_splash_add_pixel_display (state->boot_splash, display);
++
++ update_display (state);
++ }
++ }
++}
++
++static void
++on_pixel_display_removed (state_t *state,
++ ply_pixel_display_t *display)
++{
++ if (state->boot_splash == NULL)
++ return;
++
++ ply_boot_splash_remove_pixel_display (state->boot_splash, display);
++}
++
++static void
++on_text_display_added (state_t *state,
++ ply_text_display_t *display)
++{
++ if (state->is_shown) {
++ if (state->boot_splash == NULL) {
++ ply_trace ("text display added before splash loaded, so loading splash now");
++ show_splash (state);
++ } else {
++ ply_trace ("text display added after splash loaded, so attaching to splash");
++ ply_boot_splash_add_text_display (state->boot_splash, display);
++
++ update_display (state);
++ }
++ }
++}
++
++static void
++on_text_display_removed (state_t *state,
++ ply_text_display_t *display)
++{
++ if (state->boot_splash == NULL)
++ return;
++
++ ply_boot_splash_remove_text_display (state->boot_splash, display);
+ }
+
+ static void
+@@ -1033,12 +1108,25 @@ load_devices (state_t *state,
+ state->device_manager = ply_device_manager_new (state->default_tty, flags);
+ state->local_console_terminal = ply_device_manager_get_default_terminal (state->device_manager);
+
+- ply_device_manager_watch_seats (state->device_manager,
+- (ply_seat_added_handler_t)
+- on_seat_added,
+- (ply_seat_removed_handler_t)
+- on_seat_removed,
+- state);
++ ply_device_manager_watch_devices (state->device_manager,
++ state->device_timeout,
++ (ply_keyboard_added_handler_t)
++ on_keyboard_added,
++ (ply_keyboard_removed_handler_t)
++ on_keyboard_removed,
++ (ply_pixel_display_added_handler_t)
++ on_pixel_display_added,
++ (ply_pixel_display_removed_handler_t)
++ on_pixel_display_removed,
++ (ply_text_display_added_handler_t)
++ on_text_display_added,
++ (ply_text_display_removed_handler_t)
++ on_text_display_removed,
++ state);
++
++ if (ply_device_manager_has_serial_consoles (state->device_manager)) {
++ state->should_force_details = true;
++ }
+ }
+
+ static void
+@@ -1507,22 +1595,52 @@ on_enter (state_t *state,
+ }
+
+ static void
+-attach_splash_to_seats (state_t *state,
+- ply_boot_splash_t *splash)
++attach_splash_to_devices (state_t *state,
++ ply_boot_splash_t *splash)
+ {
+- ply_list_t *seats;
++ ply_list_t *keyboards;
++ ply_list_t *pixel_displays;
++ ply_list_t *text_displays;
+ ply_list_node_t *node;
+
+- seats = ply_device_manager_get_seats (state->device_manager);
+- node = ply_list_get_first_node (seats);
++ keyboards = ply_device_manager_get_keyboards (state->device_manager);
++ node = ply_list_get_first_node (keyboards);
++ while (node != NULL) {
++ ply_keyboard_t *keyboard;
++ ply_list_node_t *next_node;
++
++ keyboard = ply_list_node_get_data (node);
++ next_node = ply_list_get_next_node (keyboards, node);
++
++ ply_boot_splash_set_keyboard (splash, keyboard);
++
++ node = next_node;
++ }
++
++ pixel_displays = ply_device_manager_get_pixel_displays (state->device_manager);
++ node = ply_list_get_first_node (pixel_displays);
+ while (node != NULL) {
+- ply_seat_t *seat;
++ ply_pixel_display_t *pixel_display;
+ ply_list_node_t *next_node;
+
+- seat = ply_list_node_get_data (node);
+- next_node = ply_list_get_next_node (seats, node);
++ pixel_display = ply_list_node_get_data (node);
++ next_node = ply_list_get_next_node (pixel_displays, node);
+
+- ply_boot_splash_attach_to_seat (splash, seat);
++ ply_boot_splash_add_pixel_display (splash, pixel_display);
++
++ node = next_node;
++ }
++
++ text_displays = ply_device_manager_get_text_displays (state->device_manager);
++ node = ply_list_get_first_node (text_displays);
++ while (node != NULL) {
++ ply_text_display_t *text_display;
++ ply_list_node_t *next_node;
++
++ text_display = ply_list_node_get_data (node);
++ next_node = ply_list_get_next_node (text_displays, node);
++
++ ply_boot_splash_add_text_display (splash, text_display);
+
+ node = next_node;
+ }
+@@ -1623,7 +1741,7 @@ show_theme (state_t *state,
+ if (splash == NULL)
+ return NULL;
+
+- attach_splash_to_seats (state, splash);
++ attach_splash_to_devices (state, splash);
+ ply_device_manager_activate_renderers (state->device_manager);
+
+ splash_mode = get_splash_mode_from_mode (state->mode);
+@@ -1641,7 +1759,6 @@ show_theme (state_t *state,
+ #endif
+
+ ply_device_manager_activate_keyboards (state->device_manager);
+- show_messages (state);
+
+ return splash;
+ }
+@@ -1978,12 +2095,15 @@ on_crash (int signum)
+ {
+ struct termios term_attributes;
+ int fd;
++ static const char *show_cursor_sequence = "\033[?25h";
+
+ fd = open ("/dev/tty1", O_RDWR | O_NOCTTY);
+ if (fd < 0) fd = open ("/dev/hvc0", O_RDWR | O_NOCTTY);
+
+ ioctl (fd, KDSETMODE, KD_TEXT);
+
++ write (fd, show_cursor_sequence, sizeof (show_cursor_sequence) - 1);
++
+ tcgetattr (fd, &term_attributes);
+
+ term_attributes.c_iflag |= BRKINT | IGNPAR | ICRNL | IXON;
+@@ -2191,6 +2311,7 @@ main (int argc,
+
+ state.progress = ply_progress_new ();
+ state.splash_delay = NAN;
++ state.device_timeout = NAN;
+
+ ply_progress_load_cache (state.progress,
+ get_cache_file_for_mode (state.mode));
+@@ -2223,6 +2344,8 @@ main (int argc,
+ state.splash_delay = NAN;
+ }
+
++ find_force_scale (&state);
++
+ load_devices (&state, device_manager_flags);
+
+ ply_trace ("entering event loop");
+diff --git a/src/plugins/controls/label/plugin.c b/src/plugins/controls/label/plugin.c
+index 5edce3b..acba52b 100644
+--- a/src/plugins/controls/label/plugin.c
++++ b/src/plugins/controls/label/plugin.c
+@@ -113,15 +113,18 @@ get_cairo_context_for_pixel_buffer (ply_label_plugin_control_t *label,
+ cairo_t *cairo_context;
+ unsigned char *data;
+ ply_rectangle_t size;
++ uint32_t scale;
+
+ data = (unsigned char *) ply_pixel_buffer_get_argb32_data (pixel_buffer);
+ ply_pixel_buffer_get_size (pixel_buffer, &size);
++ scale = ply_pixel_buffer_get_device_scale (pixel_buffer);
+
+ cairo_surface = cairo_image_surface_create_for_data (data,
+ CAIRO_FORMAT_ARGB32,
+- size.width,
+- size.height,
+- size.width * 4);
++ size.width * scale,
++ size.height * scale,
++ size.width * scale * 4);
++ cairo_surface_set_device_scale (cairo_surface, scale, scale);
+ cairo_context = cairo_create (cairo_surface);
+ cairo_surface_destroy (cairo_surface);
+
+diff --git a/src/plugins/renderers/drm/Makefile.am b/src/plugins/renderers/drm/Makefile.am
+index 747f202..271b17f 100644
+--- a/src/plugins/renderers/drm/Makefile.am
++++ b/src/plugins/renderers/drm/Makefile.am
+@@ -16,12 +16,7 @@ drm_la_LDFLAGS = -module -avoid-version -export-dynamic
+ drm_la_LIBADD = $(PLYMOUTH_LIBS) $(DRM_LIBS) \
+ ../../../libply/libply.la \
+ ../../../libply-splash-core/libply-splash-core.la
+-drm_la_SOURCES = $(srcdir)/plugin.c \
+- $(srcdir)/ply-renderer-driver.h
+-drm_la_SOURCES += $(srcdir)/ply-renderer-generic-driver.h \
+- $(srcdir)/ply-renderer-generic-driver.c
+-
+-
++drm_la_SOURCES = $(srcdir)/plugin.c
+
+ endif
+
+diff --git a/src/plugins/renderers/drm/plugin.c b/src/plugins/renderers/drm/plugin.c
+index 45aab7d..b93e8e4 100644
+--- a/src/plugins/renderers/drm/plugin.c
++++ b/src/plugins/renderers/drm/plugin.c
+@@ -54,12 +54,11 @@
+ #include "ply-hashtable.h"
+ #include "ply-rectangle.h"
+ #include "ply-region.h"
++#include "ply-utils.h"
+ #include "ply-terminal.h"
+
+ #include "ply-renderer.h"
+ #include "ply-renderer-plugin.h"
+-#include "ply-renderer-driver.h"
+-#include "ply-renderer-generic-driver.h"
+
+ #define BYTES_PER_PIXEL (4)
+
+@@ -92,15 +91,27 @@ struct _ply_renderer_input_source
+ void *user_data;
+ };
+
++typedef struct
++{
++ uint32_t id;
++
++ uint32_t handle;
++ uint32_t width;
++ uint32_t height;
++ uint32_t row_stride;
++
++ void *map_address;
++ uint32_t map_size;
++ int map_count;
++
++ uint32_t added_fb : 1;
++} ply_renderer_buffer_t;
++
+ struct _ply_renderer_backend
+ {
+ ply_event_loop_t *loop;
+ ply_terminal_t *terminal;
+
+- ply_renderer_driver_interface_t *driver_interface;
+- ply_renderer_driver_t *driver;
+- uint32_t driver_supports_mapping_console;
+-
+ int device_fd;
+ char *device_name;
+ drmModeRes *resources;
+@@ -109,11 +120,14 @@ struct _ply_renderer_backend
+ ply_list_t *heads;
+ ply_hashtable_t *heads_by_connector_id;
+
++ ply_hashtable_t *output_buffers;
++
+ int32_t dither_red;
+ int32_t dither_green;
+ int32_t dither_blue;
+
+ uint32_t is_active : 1;
++ uint32_t requires_explicit_flushing : 1;
+ };
+
+ ply_renderer_plugin_interface_t *ply_renderer_backend_get_interface (void);
+@@ -127,6 +141,233 @@ static void flush_head (ply_renderer_backend_t *backend,
+ ply_renderer_head_t *head);
+
+ static bool
++ply_renderer_buffer_map (ply_renderer_backend_t *backend,
++ ply_renderer_buffer_t *buffer)
++{
++ struct drm_mode_map_dumb map_dumb_buffer_request;
++ void *map_address;
++
++ if (buffer->map_address != MAP_FAILED) {
++ buffer->map_count++;
++ return true;
++ }
++
++ memset (&map_dumb_buffer_request, 0, sizeof(struct drm_mode_map_dumb));
++ map_dumb_buffer_request.handle = buffer->handle;
++ if (drmIoctl (backend->device_fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb_buffer_request) < 0) {
++ ply_trace ("Could not map GEM object %u: %m", buffer->handle);
++ return false;
++ }
++
++ map_address = mmap (0, buffer->map_size,
++ PROT_READ | PROT_WRITE, MAP_SHARED,
++ backend->device_fd, map_dumb_buffer_request.offset);
++
++ if (map_address == MAP_FAILED)
++ return false;
++
++ buffer->map_address = map_address;
++ buffer->map_count++;
++
++ return true;
++}
++
++static void
++ply_renderer_buffer_unmap (ply_renderer_backend_t *backend,
++ ply_renderer_buffer_t *buffer)
++{
++ buffer->map_count--;
++
++ assert (buffer->map_count >= 0);
++}
++
++static ply_renderer_buffer_t *
++ply_renderer_buffer_new (ply_renderer_backend_t *backend,
++ uint32_t width,
++ uint32_t height)
++{
++ ply_renderer_buffer_t *buffer;
++ struct drm_mode_create_dumb create_dumb_buffer_request;
++
++ buffer = calloc (1, sizeof(ply_renderer_buffer_t));
++ buffer->width = width;
++ buffer->height = height;
++ buffer->map_address = MAP_FAILED;
++
++ memset (&create_dumb_buffer_request, 0, sizeof(struct drm_mode_create_dumb));
++
++ create_dumb_buffer_request.width = width;
++ create_dumb_buffer_request.height = height;
++ create_dumb_buffer_request.bpp = 32;
++ create_dumb_buffer_request.flags = 0;
++
++ if (drmIoctl (backend->device_fd,
++ DRM_IOCTL_MODE_CREATE_DUMB,
++ &create_dumb_buffer_request) < 0) {
++ free (buffer);
++ ply_trace ("Could not allocate GEM object for frame buffer: %m");
++ return NULL;
++ }
++
++ buffer->handle = create_dumb_buffer_request.handle;
++ buffer->row_stride = create_dumb_buffer_request.pitch;
++ buffer->map_size = create_dumb_buffer_request.size;
++
++ ply_trace ("returning %ux%u buffer with stride %u",
++ width, height, buffer->row_stride);
++
++ return buffer;
++}
++
++static void
++ply_renderer_buffer_free (ply_renderer_backend_t *backend,
++ ply_renderer_buffer_t *buffer)
++{
++ struct drm_mode_destroy_dumb destroy_dumb_buffer_request;
++
++ if (buffer->added_fb)
++ drmModeRmFB (backend->device_fd, buffer->id);
++
++ if (buffer->map_address != MAP_FAILED) {
++ munmap (buffer->map_address, buffer->map_size);
++ buffer->map_address = MAP_FAILED;
++ }
++
++ memset (&destroy_dumb_buffer_request, 0, sizeof(struct drm_mode_destroy_dumb));
++ destroy_dumb_buffer_request.handle = buffer->handle;
++
++ if (drmIoctl (backend->device_fd,
++ DRM_IOCTL_MODE_DESTROY_DUMB,
++ &destroy_dumb_buffer_request) < 0)
++ ply_trace ("Could not deallocate GEM object %u: %m", buffer->handle);
++
++ free (buffer);
++}
++
++static ply_renderer_buffer_t *
++get_buffer_from_id (ply_renderer_backend_t *backend,
++ uint32_t id)
++{
++ static ply_renderer_buffer_t *buffer;
++
++ buffer = ply_hashtable_lookup (backend->output_buffers, (void *) (uintptr_t) id);
++
++ return buffer;
++}
++
++static uint32_t
++create_output_buffer (ply_renderer_backend_t *backend,
++ unsigned long width,
++ unsigned long height,
++ unsigned long *row_stride)
++{
++ ply_renderer_buffer_t *buffer;
++
++ buffer = ply_renderer_buffer_new (backend, width, height);
++
++ if (buffer == NULL) {
++ ply_trace ("Could not allocate GEM object for frame buffer: %m");
++ return 0;
++ }
++
++ if (drmModeAddFB (backend->device_fd, width, height,
++ 24, 32, buffer->row_stride, buffer->handle,
++ &buffer->id) != 0) {
++ ply_trace ("Could not set up GEM object as frame buffer: %m");
++ ply_renderer_buffer_free (backend, buffer);
++ return 0;
++ }
++
++ *row_stride = buffer->row_stride;
++
++ buffer->added_fb = true;
++ ply_hashtable_insert (backend->output_buffers,
++ (void *) (uintptr_t) buffer->id,
++ buffer);
++
++ return buffer->id;
++}
++
++static bool
++map_buffer (ply_renderer_backend_t *backend,
++ uint32_t buffer_id)
++{
++ ply_renderer_buffer_t *buffer;
++
++ buffer = get_buffer_from_id (backend, buffer_id);
++
++ assert (buffer != NULL);
++
++ return ply_renderer_buffer_map (backend, buffer);
++}
++
++static void
++unmap_buffer (ply_renderer_backend_t *backend,
++ uint32_t buffer_id)
++{
++ ply_renderer_buffer_t *buffer;
++
++ buffer = get_buffer_from_id (backend, buffer_id);
++
++ assert (buffer != NULL);
++
++ ply_renderer_buffer_unmap (backend, buffer);
++}
++
++static char *
++begin_flush (ply_renderer_backend_t *backend,
++ uint32_t buffer_id)
++{
++ ply_renderer_buffer_t *buffer;
++
++ buffer = get_buffer_from_id (backend, buffer_id);
++
++ assert (buffer != NULL);
++
++ return buffer->map_address;
++}
++
++static void
++end_flush (ply_renderer_backend_t *backend,
++ uint32_t buffer_id)
++{
++ ply_renderer_buffer_t *buffer;
++
++ buffer = get_buffer_from_id (backend, buffer_id);
++
++ assert (buffer != NULL);
++
++ if (backend->requires_explicit_flushing) {
++ struct drm_clip_rect flush_area;
++ int ret;
++
++ flush_area.x1 = 0;
++ flush_area.y1 = 0;
++ flush_area.x2 = buffer->width;
++ flush_area.y2 = buffer->height;
++
++ ret = drmModeDirtyFB (backend->device_fd, buffer->id, &flush_area, 1);
++
++ if (ret == -ENOSYS)
++ backend->requires_explicit_flushing = false;
++ }
++}
++
++static void
++destroy_output_buffer (ply_renderer_backend_t *backend,
++ uint32_t buffer_id)
++{
++ ply_renderer_buffer_t *buffer;
++
++ buffer = ply_hashtable_remove (backend->output_buffers,
++ (void *) (uintptr_t) buffer_id);
++
++ assert (buffer != NULL);
++
++ ply_renderer_buffer_free (backend, buffer);
++}
++
++static bool
+ ply_renderer_head_add_connector (ply_renderer_head_t *head,
+ drmModeConnector *connector,
+ int connector_mode_index)
+@@ -185,6 +426,11 @@ ply_renderer_head_new (ply_renderer_backend_t *backend,
+ assert (ply_array_get_size (head->connector_ids) > 0);
+
+ head->pixel_buffer = ply_pixel_buffer_new (head->area.width, head->area.height);
++ ply_pixel_buffer_set_device_scale (head->pixel_buffer,
++ ply_get_device_scale (head->area.width,
++ head->area.height,
++ connector->mmWidth,
++ connector->mmHeight));
+
+ ply_trace ("Creating %ldx%ld renderer head", head->area.width, head->area.height);
+ ply_pixel_buffer_fill_with_color (head->pixel_buffer, NULL,
+@@ -241,25 +487,21 @@ ply_renderer_head_map (ply_renderer_backend_t *backend,
+
+ assert (backend != NULL);
+ assert (backend->device_fd >= 0);
+- assert (backend->driver_interface != NULL);
+- assert (backend->driver != NULL);
++ assert (backend != NULL);
+
+ assert (head != NULL);
+
+ ply_trace ("Creating buffer for %ldx%ld renderer head", head->area.width, head->area.height);
+- head->scan_out_buffer_id =
+- backend->driver_interface->create_buffer (backend->driver,
+- head->area.width, head->area.height,
+- &head->row_stride);
++ head->scan_out_buffer_id = create_output_buffer (backend,
++ head->area.width, head->area.height,
++ &head->row_stride);
+
+ if (head->scan_out_buffer_id == 0)
+ return false;
+
+ ply_trace ("Mapping buffer for %ldx%ld renderer head", head->area.width, head->area.height);
+- if (!backend->driver_interface->map_buffer (backend->driver,
+- head->scan_out_buffer_id)) {
+- backend->driver_interface->destroy_buffer (backend->driver,
+- head->scan_out_buffer_id);
++ if (!map_buffer (backend, head->scan_out_buffer_id)) {
++ destroy_output_buffer (backend, head->scan_out_buffer_id);
+ head->scan_out_buffer_id = 0;
+ return false;
+ }
+@@ -271,8 +513,7 @@ ply_renderer_head_map (ply_renderer_backend_t *backend,
+
+ scan_out_set = reset_scan_out_buffer_if_needed (backend, head);
+ if (!scan_out_set && backend->is_active) {
+- backend->driver_interface->destroy_buffer (backend->driver,
+- head->scan_out_buffer_id);
++ destroy_output_buffer (backend, head->scan_out_buffer_id);
+ head->scan_out_buffer_id = 0;
+ return false;
+ }
+@@ -285,11 +526,9 @@ ply_renderer_head_unmap (ply_renderer_backend_t *backend,
+ ply_renderer_head_t *head)
+ {
+ ply_trace ("unmapping %ldx%ld renderer head", head->area.width, head->area.height);
+- backend->driver_interface->unmap_buffer (backend->driver,
+- head->scan_out_buffer_id);
++ unmap_buffer (backend, head->scan_out_buffer_id);
+
+- backend->driver_interface->destroy_buffer (backend->driver,
+- head->scan_out_buffer_id);
++ destroy_output_buffer (backend, head->scan_out_buffer_id);
+ head->scan_out_buffer_id = 0;
+ }
+
+@@ -375,10 +614,19 @@ create_backend (const char *device_name,
+ backend->heads = ply_list_new ();
+ backend->input_source.key_buffer = ply_buffer_new ();
+ backend->terminal = terminal;
++ backend->requires_explicit_flushing = true;
++ backend->output_buffers = ply_hashtable_new (ply_hashtable_direct_hash,
++ ply_hashtable_direct_compare);
+
+ return backend;
+ }
+
++static const char *
++get_device_name (ply_renderer_backend_t *backend)
++{
++ return backend->device_name;
++}
++
+ static void
+ destroy_backend (ply_renderer_backend_t *backend)
+ {
+@@ -386,6 +634,9 @@ destroy_backend (ply_renderer_backend_t *backend)
+ free_heads (backend);
+
+ free (backend->device_name);
++ ply_hashtable_free (backend->output_buffers);
++
++ drmModeFreeResources (backend->resources);
+
+ free (backend);
+ }
+@@ -455,44 +706,29 @@ load_driver (ply_renderer_backend_t *backend)
+ return false;
+ }
+
+- backend->driver_interface = ply_renderer_generic_driver_get_interface (device_fd);
+- backend->driver_supports_mapping_console = false;
+-
+- if (backend->driver_interface == NULL) {
+- close (device_fd);
+- return false;
+- }
+-
+- backend->driver = backend->driver_interface->create_driver (device_fd);
+-
+- if (backend->driver == NULL) {
+- close (device_fd);
+- return false;
+- }
+-
+ backend->device_fd = device_fd;
+
++ drmDropMaster (device_fd);
++
+ return true;
+ }
+
+ static void
+-unload_driver (ply_renderer_backend_t *backend)
++unload_backend (ply_renderer_backend_t *backend)
+ {
+- if (backend->driver == NULL)
++ if (backend == NULL)
+ return;
+
+- ply_trace ("unloading driver");
+- assert (backend->driver_interface != NULL);
+-
+- backend->driver_interface->destroy_driver (backend->driver);
+- backend->driver = NULL;
+-
+- backend->driver_interface = NULL;
++ ply_trace ("unloading backend");
+
+ if (backend->device_fd >= 0) {
+ drmClose (backend->device_fd);
+ backend->device_fd = -1;
+ }
++
++ destroy_backend (backend);
++ backend = NULL;
++
+ }
+
+ static bool
+@@ -540,7 +776,7 @@ close_device (ply_renderer_backend_t *backend)
+ backend);
+ }
+
+- unload_driver (backend);
++ unload_backend (backend);
+ }
+
+ static drmModeCrtc *
+@@ -732,34 +968,6 @@ create_heads_for_active_connectors (ply_renderer_backend_t *backend)
+
+ ply_hashtable_free (heads_by_controller_id);
+
+-#ifdef PLY_ENABLE_DEPRECATED_GDM_TRANSITION
+- /* If the driver doesn't support mapping the fb console
+- * then we can't get a smooth crossfade transition to
+- * the display manager unless we use the /dev/fb interface
+- * or the plymouth deactivate interface.
+- *
+- * In multihead configurations, we'd rather have working
+- * multihead, but otherwise bail now.
+- */
+- if (!backend->driver_supports_mapping_console &&
+- ply_list_get_length (backend->heads) == 1) {
+- ply_list_node_t *node;
+- ply_renderer_head_t *head;
+-
+- node = ply_list_get_first_node (backend->heads);
+- head = (ply_renderer_head_t *) ply_list_node_get_data (node);
+-
+- if (ply_array_get_size (head->connector_ids) == 1) {
+- ply_trace ("Only one monitor configured, and driver doesn't "
+- "support mapping console, so letting frame-buffer "
+- "take over");
+-
+- free_heads (backend);
+- return false;
+- }
+- }
+-#endif
+-
+ return ply_list_get_length (backend->heads) > 0;
+ }
+
+@@ -774,7 +982,7 @@ has_32bpp_support (ply_renderer_backend_t *backend)
+ min_width = backend->resources->min_width;
+ min_height = backend->resources->min_height;
+
+- /* Some drivers set min_width/min_height to 0,
++ /* Some backends set min_width/min_height to 0,
+ * but 0x0 sized buffers don't work.
+ */
+ if (min_width == 0)
+@@ -783,10 +991,7 @@ has_32bpp_support (ply_renderer_backend_t *backend)
+ if (min_height == 0)
+ min_height = 1;
+
+- buffer_id = backend->driver_interface->create_buffer (backend->driver,
+- min_width,
+- min_height,
+- &row_stride);
++ buffer_id = create_output_buffer (backend, min_width, min_height, &row_stride);
+
+ if (buffer_id == 0) {
+ ply_trace ("Could not create minimal (%ux%u) 32bpp dummy buffer",
+@@ -795,8 +1000,7 @@ has_32bpp_support (ply_renderer_backend_t *backend)
+ return false;
+ }
+
+- backend->driver_interface->destroy_buffer (backend->driver,
+- buffer_id);
++ destroy_output_buffer (backend, buffer_id);
+
+ return true;
+ }
+@@ -860,93 +1064,10 @@ map_to_device (ply_renderer_backend_t *backend)
+ return head_mapped;
+ }
+
+-static bool
+-ply_renderer_head_set_scan_out_buffer_to_console (ply_renderer_backend_t *backend,
+- ply_renderer_head_t *head,
+- bool should_set_to_black)
+-{
+- unsigned long width;
+- unsigned long height;
+- unsigned long row_stride;
+- uint32_t *shadow_buffer;
+- ply_pixel_buffer_t *pixel_buffer;
+- char *map_address;
+- ply_rectangle_t area;
+-
+- if (!backend->driver_interface->fetch_buffer (backend->driver,
+- head->console_buffer_id,
+- &width, &height, &row_stride))
+- return false;
+-
+- if (!backend->driver_interface->map_buffer (backend->driver,
+- head->console_buffer_id)) {
+- backend->driver_interface->destroy_buffer (backend->driver,
+- head->console_buffer_id);
+- return false;
+- }
+-
+- if (head->area.width != width || head->area.height != height) {
+- /* Force black if the fb console resolution doesn't match our resolution
+- */
+- area.x = 0;
+- area.y = 0;
+- area.width = width;
+- area.height = height;
+-
+- should_set_to_black = true;
+- ply_trace ("Console fb is %ldx%ld and screen contents are %ldx%ld. "
+- "They aren't the same dimensions; forcing black",
+- width, height, head->area.width, head->area.height);
+- } else {
+- area = head->area;
+- }
+-
+- if (should_set_to_black) {
+- pixel_buffer = ply_pixel_buffer_new (width, height);
+- shadow_buffer = ply_pixel_buffer_get_argb32_data (pixel_buffer);
+- } else {
+- pixel_buffer = NULL;
+- shadow_buffer = ply_pixel_buffer_get_argb32_data (head->pixel_buffer);
+- }
+-
+- ply_trace ("Drawing %s to console fb", should_set_to_black ? "black" : "screen contents");
+- map_address =
+- backend->driver_interface->begin_flush (backend->driver,
+- head->console_buffer_id);
+-
+- flush_area ((char *) shadow_buffer, area.width * 4,
+- map_address, row_stride, &area);
+-
+- backend->driver_interface->end_flush (backend->driver,
+- head->console_buffer_id);
+-
+- backend->driver_interface->unmap_buffer (backend->driver,
+- head->console_buffer_id);
+-
+- ply_trace ("Setting scan out hardware to console fb");
+- ply_renderer_head_set_scan_out_buffer (backend,
+- head, head->console_buffer_id);
+-
+- backend->driver_interface->destroy_buffer (backend->driver,
+- head->console_buffer_id);
+-
+- if (pixel_buffer != NULL)
+- ply_pixel_buffer_free (pixel_buffer);
+-
+- return true;
+-}
+-
+ static void
+ unmap_from_device (ply_renderer_backend_t *backend)
+ {
+ ply_list_node_t *node;
+- bool should_set_to_black;
+-
+- /* We only copy what's on screen back to the fb console
+- * if there's one head (since in multihead set ups the fb console
+- * is cloned).
+- */
+- should_set_to_black = ply_list_get_length (backend->heads) > 1;
+
+ node = ply_list_get_first_node (backend->heads);
+ while (node != NULL) {
+@@ -956,13 +1077,6 @@ unmap_from_device (ply_renderer_backend_t *backend)
+ head = (ply_renderer_head_t *) ply_list_node_get_data (node);
+ next_node = ply_list_get_next_node (backend->heads, node);
+
+- if (backend->is_active) {
+- ply_trace ("scanning out %s directly to console",
+- should_set_to_black ? "black" : "splash");
+- ply_renderer_head_set_scan_out_buffer_to_console (backend, head,
+- should_set_to_black);
+- }
+-
+ ply_renderer_head_unmap (backend, head);
+
+ node = next_node;
+@@ -1019,9 +1133,7 @@ flush_head (ply_renderer_backend_t *backend,
+ updated_region = ply_pixel_buffer_get_updated_areas (pixel_buffer);
+ areas_to_flush = ply_region_get_sorted_rectangle_list (updated_region);
+
+- map_address =
+- backend->driver_interface->begin_flush (backend->driver,
+- head->scan_out_buffer_id);
++ map_address = begin_flush (backend, head->scan_out_buffer_id);
+
+ node = ply_list_get_first_node (areas_to_flush);
+ while (node != NULL) {
+@@ -1041,8 +1153,7 @@ flush_head (ply_renderer_backend_t *backend,
+ node = next_node;
+ }
+
+- backend->driver_interface->end_flush (backend->driver,
+- head->scan_out_buffer_id);
++ end_flush (backend, head->scan_out_buffer_id);
+
+ ply_region_clear (updated_region);
+ }
+@@ -1180,7 +1291,8 @@ ply_renderer_backend_get_interface (void)
+ .get_input_source = get_input_source,
+ .open_input_source = open_input_source,
+ .set_handler_for_input_source = set_handler_for_input_source,
+- .close_input_source = close_input_source
++ .close_input_source = close_input_source,
++ .get_device_name = get_device_name
+ };
+
+ return &plugin_interface;
+diff --git a/src/plugins/renderers/drm/ply-renderer-driver.h b/src/plugins/renderers/drm/ply-renderer-driver.h
+deleted file mode 100644
+index f3febcf..0000000
+--- a/src/plugins/renderers/drm/ply-renderer-driver.h
++++ /dev/null
+@@ -1,66 +0,0 @@
+-/* ply-renderer-driver.h
+- *
+- * Copyright (C) 2009 Red Hat, Inc.
+- *
+- * This program is free software; you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License as published by
+- * the Free Software Foundation; either version 2, or (at your option)
+- * any later version.
+- *
+- * This program is distributed in the hope that it will be useful,
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- * GNU General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with this program; if not, write to the Free Software
+- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+- * 02111-1307, USA.
+- *
+- * Written By: Ray Strode <rstrode@redhat.com>
+- */
+-#ifndef PLY_RENDERER_DRIVER_H
+-#define PLY_RENDERER_DRIVER_H
+-
+-#include <stdbool.h>
+-#include <stdint.h>
+-
+-#include "ply-list.h"
+-#include "ply-rectangle.h"
+-#include "ply-utils.h"
+-
+-typedef struct _ply_renderer_driver ply_renderer_driver_t;
+-
+-typedef struct
+-{
+- ply_renderer_driver_t * (*create_driver)(int device_fd);
+-
+- void (*destroy_driver)(ply_renderer_driver_t *driver);
+-
+- uint32_t (*create_buffer)(ply_renderer_driver_t *driver,
+- unsigned long width,
+- unsigned long height,
+- unsigned long *row_stride);
+- bool (*fetch_buffer)(ply_renderer_driver_t *driver,
+- uint32_t buffer_id,
+- unsigned long *width,
+- unsigned long *height,
+- unsigned long *row_stride);
+-
+- bool (*map_buffer)(ply_renderer_driver_t *driver,
+- uint32_t buffer_id);
+-
+- void (*unmap_buffer)(ply_renderer_driver_t *driver,
+- uint32_t buffer_id);
+-
+- char * (*begin_flush)(ply_renderer_driver_t * driver,
+- uint32_t buffer_id);
+- void (*end_flush)(ply_renderer_driver_t *driver,
+- uint32_t buffer_id);
+-
+- void (*destroy_buffer)(ply_renderer_driver_t *driver,
+- uint32_t buffer_id);
+-} ply_renderer_driver_interface_t;
+-
+-#endif /* PLY_RENDERER_DRIVER_H */
+-/* vim: set ts=4 sw=4 et ai ci cino={.5s,^-2,+.5s,t0,g0,e-2,n-2,p2s,(0,=.5s,:.5s */
+diff --git a/src/plugins/renderers/drm/ply-renderer-generic-driver.c b/src/plugins/renderers/drm/ply-renderer-generic-driver.c
+deleted file mode 100644
+index b1be714..0000000
+--- a/src/plugins/renderers/drm/ply-renderer-generic-driver.c
++++ /dev/null
+@@ -1,385 +0,0 @@
+-/* ply-renderer-generic-driver.c - interface to generic drm kms api
+- *
+- * Copyright (C) 2012 Red Hat, Inc.
+- *
+- * This program is free software; you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License as published by
+- * the Free Software Foundation; either version 2, or (at your option)
+- * any later version.
+- *
+- * This program is distributed in the hope that it will be useful,
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- * GNU General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with this program; if not, write to the Free Software
+- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+- * 02111-1307, USA.
+- *
+- * Written by: Dave Airlie
+- * Based on other renderer drivers written by Ray Strode
+- */
+-#include "config.h"
+-
+-#include "ply-renderer-generic-driver.h"
+-
+-#include <arpa/inet.h>
+-#include <assert.h>
+-#include <errno.h>
+-#include <fcntl.h>
+-#include <signal.h>
+-#include <string.h>
+-#include <stdbool.h>
+-#include <stdint.h>
+-#include <stdio.h>
+-#include <stdlib.h>
+-#include <sys/ioctl.h>
+-#include <sys/mman.h>
+-#include <sys/stat.h>
+-#include <sys/types.h>
+-#include <values.h>
+-#include <unistd.h>
+-
+-#include <drm.h>
+-#include <xf86drm.h>
+-#include <xf86drmMode.h>
+-
+-#include "ply-hashtable.h"
+-#include "ply-logger.h"
+-#include "ply-renderer-driver.h"
+-
+-typedef struct _ply_renderer_buffer ply_renderer_buffer_t;
+-
+-struct _ply_renderer_buffer
+-{
+- uint32_t id;
+-
+- uint32_t handle;
+- uint32_t width;
+- uint32_t height;
+- uint32_t row_stride;
+-
+- void *map_address;
+- uint32_t map_size;
+- int map_count;
+-
+- uint32_t added_fb : 1;
+-};
+-
+-struct _ply_renderer_driver
+-{
+- int device_fd;
+- ply_hashtable_t *buffers;
+-
+- uint32_t requires_explicit_flushing : 1;
+-};
+-
+-static bool
+-ply_renderer_buffer_map (ply_renderer_driver_t *driver,
+- ply_renderer_buffer_t *buffer)
+-{
+- struct drm_mode_map_dumb map_dumb_buffer_request;
+- void *map_address;
+-
+- if (buffer->map_address != MAP_FAILED) {
+- buffer->map_count++;
+- return true;
+- }
+-
+- memset (&map_dumb_buffer_request, 0, sizeof(struct drm_mode_map_dumb));
+- map_dumb_buffer_request.handle = buffer->handle;
+- if (drmIoctl (driver->device_fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb_buffer_request) < 0) {
+- ply_trace ("Could not map GEM object %u: %m", buffer->handle);
+- return false;
+- }
+-
+- map_address = mmap (0, buffer->map_size,
+- PROT_READ | PROT_WRITE, MAP_SHARED,
+- driver->device_fd, map_dumb_buffer_request.offset);
+-
+- if (map_address == MAP_FAILED)
+- return false;
+-
+- buffer->map_address = map_address;
+- buffer->map_count++;
+-
+- return true;
+-}
+-
+-static void
+-ply_renderer_buffer_unmap (ply_renderer_driver_t *driver,
+- ply_renderer_buffer_t *buffer)
+-{
+- buffer->map_count--;
+-
+- assert (buffer->map_count >= 0);
+-}
+-
+-static ply_renderer_driver_t *
+-create_driver (int device_fd)
+-{
+- ply_renderer_driver_t *driver;
+-
+- driver = calloc (1, sizeof(ply_renderer_driver_t));
+- driver->device_fd = device_fd;
+- driver->requires_explicit_flushing = true;
+- driver->buffers = ply_hashtable_new (ply_hashtable_direct_hash,
+- ply_hashtable_direct_compare);
+-
+- return driver;
+-}
+-
+-static void
+-destroy_driver (ply_renderer_driver_t *driver)
+-{
+- ply_hashtable_free (driver->buffers);
+-
+- free (driver);
+-}
+-
+-static ply_renderer_buffer_t *
+-ply_renderer_buffer_new (ply_renderer_driver_t *driver,
+- uint32_t width,
+- uint32_t height)
+-{
+- ply_renderer_buffer_t *buffer;
+- struct drm_mode_create_dumb create_dumb_buffer_request;
+-
+- buffer = calloc (1, sizeof(ply_renderer_buffer_t));
+- buffer->width = width;
+- buffer->height = height;
+- buffer->map_address = MAP_FAILED;
+-
+- memset (&create_dumb_buffer_request, 0, sizeof(struct drm_mode_create_dumb));
+-
+- create_dumb_buffer_request.width = width;
+- create_dumb_buffer_request.height = height;
+- create_dumb_buffer_request.bpp = 32;
+- create_dumb_buffer_request.flags = 0;
+-
+- if (drmIoctl (driver->device_fd,
+- DRM_IOCTL_MODE_CREATE_DUMB,
+- &create_dumb_buffer_request) < 0) {
+- free (buffer);
+- ply_trace ("Could not allocate GEM object for frame buffer: %m");
+- return NULL;
+- }
+-
+- buffer->handle = create_dumb_buffer_request.handle;
+- buffer->row_stride = create_dumb_buffer_request.pitch;
+- buffer->map_size = create_dumb_buffer_request.size;
+-
+- ply_trace ("returning %ux%u buffer with stride %u",
+- width, height, buffer->row_stride);
+-
+- return buffer;
+-}
+-
+-static void
+-ply_renderer_buffer_free (ply_renderer_driver_t *driver,
+- ply_renderer_buffer_t *buffer)
+-{
+- struct drm_mode_destroy_dumb destroy_dumb_buffer_request;
+-
+- if (buffer->added_fb)
+- drmModeRmFB (driver->device_fd, buffer->id);
+-
+- if (buffer->map_address != MAP_FAILED) {
+- munmap (buffer->map_address, buffer->map_size);
+- buffer->map_address = MAP_FAILED;
+- }
+-
+- memset (&destroy_dumb_buffer_request, 0, sizeof(struct drm_mode_destroy_dumb));
+- destroy_dumb_buffer_request.handle = buffer->handle;
+-
+- if (drmIoctl (driver->device_fd,
+- DRM_IOCTL_MODE_DESTROY_DUMB,
+- &destroy_dumb_buffer_request) < 0)
+- ply_trace ("Could not deallocate GEM object %u: %m", buffer->handle);
+-
+- free (buffer);
+-}
+-
+-static ply_renderer_buffer_t *
+-get_buffer_from_id (ply_renderer_driver_t *driver,
+- uint32_t id)
+-{
+- static ply_renderer_buffer_t *buffer;
+-
+- buffer = ply_hashtable_lookup (driver->buffers, (void *) (uintptr_t) id);
+-
+- return buffer;
+-}
+-
+-static bool
+-fetch_buffer (ply_renderer_driver_t *driver,
+- uint32_t buffer_id,
+- unsigned long *width,
+- unsigned long *height,
+- unsigned long *row_stride)
+-{
+- ply_renderer_buffer_t *buffer;
+-
+- buffer = get_buffer_from_id (driver, buffer_id);
+-
+- if (buffer == NULL) {
+- ply_trace ("could not fetch buffer %u", buffer_id);
+- return false;
+- }
+-
+- if (width != NULL)
+- *width = buffer->width;
+-
+- if (height != NULL)
+- *height = buffer->height;
+-
+- if (row_stride != NULL)
+- *row_stride = buffer->row_stride;
+-
+- ply_trace ("fetched %ux%u buffer with stride %u",
+- buffer->width, buffer->height, buffer->row_stride);
+- return true;
+-}
+-
+-static uint32_t
+-create_buffer (ply_renderer_driver_t *driver,
+- unsigned long width,
+- unsigned long height,
+- unsigned long *row_stride)
+-{
+- ply_renderer_buffer_t *buffer;
+-
+- buffer = ply_renderer_buffer_new (driver, width, height);
+-
+- if (buffer == NULL) {
+- ply_trace ("Could not allocate GEM object for frame buffer: %m");
+- return 0;
+- }
+-
+- if (drmModeAddFB (driver->device_fd, width, height,
+- 24, 32, buffer->row_stride, buffer->handle,
+- &buffer->id) != 0) {
+- ply_trace ("Could not set up GEM object as frame buffer: %m");
+- ply_renderer_buffer_free (driver, buffer);
+- return 0;
+- }
+-
+- *row_stride = buffer->row_stride;
+-
+- buffer->added_fb = true;
+- ply_hashtable_insert (driver->buffers,
+- (void *) (uintptr_t) buffer->id,
+- buffer);
+-
+- return buffer->id;
+-}
+-
+-static bool
+-map_buffer (ply_renderer_driver_t *driver,
+- uint32_t buffer_id)
+-{
+- ply_renderer_buffer_t *buffer;
+-
+- buffer = get_buffer_from_id (driver, buffer_id);
+-
+- assert (buffer != NULL);
+-
+- return ply_renderer_buffer_map (driver, buffer);
+-}
+-
+-static void
+-unmap_buffer (ply_renderer_driver_t *driver,
+- uint32_t buffer_id)
+-{
+- ply_renderer_buffer_t *buffer;
+-
+- buffer = get_buffer_from_id (driver, buffer_id);
+-
+- assert (buffer != NULL);
+-
+- ply_renderer_buffer_unmap (driver, buffer);
+-}
+-
+-static char *
+-begin_flush (ply_renderer_driver_t *driver,
+- uint32_t buffer_id)
+-{
+- ply_renderer_buffer_t *buffer;
+-
+- buffer = get_buffer_from_id (driver, buffer_id);
+-
+- assert (buffer != NULL);
+-
+- return buffer->map_address;
+-}
+-
+-static void
+-end_flush (ply_renderer_driver_t *driver,
+- uint32_t buffer_id)
+-{
+- ply_renderer_buffer_t *buffer;
+-
+- buffer = get_buffer_from_id (driver, buffer_id);
+-
+- assert (buffer != NULL);
+-
+- if (driver->requires_explicit_flushing) {
+- struct drm_clip_rect flush_area;
+- int ret;
+-
+- flush_area.x1 = 0;
+- flush_area.y1 = 0;
+- flush_area.x2 = buffer->width;
+- flush_area.y2 = buffer->height;
+-
+- ret = drmModeDirtyFB (driver->device_fd, buffer->id, &flush_area, 1);
+-
+- if (ret == -ENOSYS)
+- driver->requires_explicit_flushing = false;
+- }
+-}
+-
+-static void
+-destroy_buffer (ply_renderer_driver_t *driver,
+- uint32_t buffer_id)
+-{
+- ply_renderer_buffer_t *buffer;
+-
+- buffer = ply_hashtable_remove (driver->buffers,
+- (void *) (uintptr_t) buffer_id);
+-
+- assert (buffer != NULL);
+-
+- ply_renderer_buffer_free (driver, buffer);
+-}
+-
+-ply_renderer_driver_interface_t *
+-ply_renderer_generic_driver_get_interface (int device_fd)
+-{
+- uint64_t supports_dumb_buffers;
+-
+- static ply_renderer_driver_interface_t driver_interface =
+- {
+- .create_driver = create_driver,
+- .destroy_driver = destroy_driver,
+- .create_buffer = create_buffer,
+- .fetch_buffer = fetch_buffer,
+- .map_buffer = map_buffer,
+- .unmap_buffer = unmap_buffer,
+- .begin_flush = begin_flush,
+- .end_flush = end_flush,
+- .destroy_buffer = destroy_buffer,
+- };
+-
+-
+- if (drmGetCap (device_fd, DRM_CAP_DUMB_BUFFER, &supports_dumb_buffers) < 0)
+- return NULL;
+-
+- if (!supports_dumb_buffers)
+- return NULL;
+-
+- return &driver_interface;
+-}
+-
+-/* vim: set ts=4 sw=4 et ai ci cino={.5s,^-2,+.5s,t0,g0,e-2,n-2,p2s, (0,=.5s,:.5s */
+diff --git a/src/plugins/renderers/drm/ply-renderer-generic-driver.h b/src/plugins/renderers/drm/ply-renderer-generic-driver.h
+deleted file mode 100644
+index 053587a..0000000
+--- a/src/plugins/renderers/drm/ply-renderer-generic-driver.h
++++ /dev/null
+@@ -1,33 +0,0 @@
+-/* ply-renderer-generic-driver.h
+- *
+- * Copyright (C) 2012 Red Hat, Inc.
+- *
+- * This program is free software; you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License as published by
+- * the Free Software Foundation; either version 2, or (at your option)
+- * any later version.
+- *
+- * This program is distributed in the hope that it will be useful,
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- * GNU General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with this program; if not, write to the Free Software
+- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+- * 02111-1307, USA.
+- *
+- * Written by: Dave Airlie
+- * Based on other renderer drivers written by Ray Strode
+- */
+-#ifndef PLY_RENDERER_DUMB_DRIVER_H
+-#define PLY_RENDERER_DUMB_DRIVER_H
+-
+-#include "ply-renderer-driver.h"
+-
+-#ifndef PLY_HIDE_FUNCTION_DECLARATIONS
+-ply_renderer_driver_interface_t *ply_renderer_generic_driver_get_interface (int device_fd);
+-#endif
+-
+-#endif /* PLY_RENDERER_GENERIC_DRIVER_H */
+-/* vim: set ts=4 sw=4 et ai ci cino={.5s,^-2,+.5s,t0,g0,e-2,n-2,p2s,(0,=.5s,:.5s */
+diff --git a/src/plugins/renderers/frame-buffer/plugin.c b/src/plugins/renderers/frame-buffer/plugin.c
+index 418855a..41ad6cd 100644
+--- a/src/plugins/renderers/frame-buffer/plugin.c
++++ b/src/plugins/renderers/frame-buffer/plugin.c
+@@ -365,6 +365,12 @@ open_device (ply_renderer_backend_t *backend)
+ return true;
+ }
+
++static const char *
++get_device_name (ply_renderer_backend_t *backend)
++{
++ return backend->device_name;
++}
++
+ static void
+ close_device (ply_renderer_backend_t *backend)
+ {
+@@ -732,7 +738,8 @@ ply_renderer_backend_get_interface (void)
+ .get_input_source = get_input_source,
+ .open_input_source = open_input_source,
+ .set_handler_for_input_source = set_handler_for_input_source,
+- .close_input_source = close_input_source
++ .close_input_source = close_input_source,
++ .get_device_name = get_device_name
+ };
+
+ return &plugin_interface;
+diff --git a/src/plugins/renderers/x11/plugin.c b/src/plugins/renderers/x11/plugin.c
+index 1e1fe31..8948f69 100644
+--- a/src/plugins/renderers/x11/plugin.c
++++ b/src/plugins/renderers/x11/plugin.c
+@@ -56,6 +56,7 @@
+ #include "ply-logger.h"
+ #include "ply-rectangle.h"
+ #include "ply-region.h"
++#include "ply-utils.h"
+
+ #include "ply-renderer.h"
+ #include "ply-renderer-plugin.h"
+@@ -64,9 +65,10 @@ struct _ply_renderer_head
+ {
+ ply_renderer_backend_t *backend;
+ ply_pixel_buffer_t *pixel_buffer;
+- ply_rectangle_t area;
++ ply_rectangle_t area; /* in device pixels */
+ GtkWidget *window;
+ cairo_surface_t *image;
++ uint32_t scale;
+ uint32_t is_fullscreen : 1;
+ };
+
+@@ -148,9 +150,14 @@ open_device (ply_renderer_backend_t *backend)
+ Display *display;
+ int display_fd;
+
++ gdk_set_allowed_backends ("x11");
++
+ if (!gtk_init_check (0, NULL))
+ return false;
+
++ /* Force gtk+ to deal in device pixels */
++ gdk_x11_display_set_window_scale (gdk_display_get_default (), 1);
++
+ display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
+ display_fd = ConnectionNumber (display);
+ backend->display_watch = ply_event_loop_watch_fd (backend->loop,
+@@ -163,6 +170,12 @@ open_device (ply_renderer_backend_t *backend)
+ return true;
+ }
+
++static const char *
++get_device_name (ply_renderer_backend_t *backend)
++{
++ return gdk_display_get_name (gdk_display_get_default ());
++}
++
+ static void
+ close_device (ply_renderer_backend_t *backend)
+ {
+@@ -183,7 +196,9 @@ create_fake_multi_head_setup (ply_renderer_backend_t *backend)
+ head->area.y = 0;
+ head->area.width = 800; /* FIXME hardcoded */
+ head->area.height = 600;
++ head->scale = 1;
+ head->pixel_buffer = ply_pixel_buffer_new (head->area.width, head->area.height);
++ ply_pixel_buffer_set_device_scale (head->pixel_buffer, head->scale);
+
+ ply_list_append_data (backend->heads, head);
+
+@@ -194,7 +209,9 @@ create_fake_multi_head_setup (ply_renderer_backend_t *backend)
+ head->area.y = 0;
+ head->area.width = 640; /* FIXME hardcoded */
+ head->area.height = 480;
++ head->scale = 1;
+ head->pixel_buffer = ply_pixel_buffer_new (head->area.width, head->area.height);
++ ply_pixel_buffer_set_device_scale (head->pixel_buffer, head->scale);
+
+ ply_list_append_data (backend->heads, head);
+ }
+@@ -204,8 +221,11 @@ create_fullscreen_single_head_setup (ply_renderer_backend_t *backend)
+ {
+ ply_renderer_head_t *head;
+ GdkRectangle monitor_geometry;
++ int width_mm, height_mm;
+
+ gdk_screen_get_monitor_geometry (gdk_screen_get_default (), 0, &monitor_geometry);
++ width_mm = gdk_screen_get_monitor_width_mm (gdk_screen_get_default (), 0);
++ height_mm = gdk_screen_get_monitor_height_mm (gdk_screen_get_default (), 0);
+
+ head = calloc (1, sizeof(ply_renderer_head_t));
+
+@@ -215,7 +235,11 @@ create_fullscreen_single_head_setup (ply_renderer_backend_t *backend)
+ head->area.width = monitor_geometry.width;
+ head->area.height = monitor_geometry.height;
+ head->is_fullscreen = true;
++ head->scale = ply_get_device_scale (monitor_geometry.width,
++ monitor_geometry.height,
++ width_mm, height_mm);
+ head->pixel_buffer = ply_pixel_buffer_new (head->area.width, head->area.height);
++ ply_pixel_buffer_set_device_scale (head->pixel_buffer, head->scale);
+
+ ply_list_append_data (backend->heads, head);
+ }
+@@ -380,6 +404,12 @@ flush_head (ply_renderer_backend_t *backend,
+ area_to_flush = (ply_rectangle_t *) ply_list_node_get_data (node);
+ next_node = ply_list_get_next_node (areas_to_flush, node);
+
++ cairo_surface_mark_dirty_rectangle (head->image,
++ area_to_flush->x,
++ area_to_flush->y,
++ area_to_flush->width,
++ area_to_flush->height);
++
+ gtk_widget_queue_draw_area (head->window,
+ area_to_flush->x,
+ area_to_flush->y,
+@@ -515,7 +545,8 @@ ply_renderer_backend_get_interface (void)
+ .get_input_source = get_input_source,
+ .open_input_source = open_input_source,
+ .set_handler_for_input_source = set_handler_for_input_source,
+- .close_input_source = close_input_source
++ .close_input_source = close_input_source,
++ .get_device_name = get_device_name
+ };
+
+ return &plugin_interface;
+diff --git a/src/plugins/splash/details/plugin.c b/src/plugins/splash/details/plugin.c
+index 8e55d05..f534450 100644
+--- a/src/plugins/splash/details/plugin.c
++++ b/src/plugins/splash/details/plugin.c
+@@ -77,6 +77,8 @@ struct _ply_boot_splash_plugin
+ ply_list_t *views;
+ ply_boot_splash_display_type_t state;
+ ply_list_t *messages;
++
++ ply_buffer_t *boot_buffer;
+ };
+
+ static view_t *
+@@ -239,6 +241,15 @@ add_text_display (ply_boot_splash_plugin_t *plugin,
+ ply_terminal_activate_vt (terminal);
+
+ ply_list_append_data (plugin->views, view);
++
++ if (plugin->boot_buffer != NULL) {
++ size_t size;
++ const char *bytes;
++
++ size = ply_buffer_get_size (plugin->boot_buffer);
++ bytes = ply_buffer_get_bytes (plugin->boot_buffer);
++ view_write (view, bytes, size);
++ }
+ }
+
+ static void
+@@ -282,8 +293,9 @@ show_splash_screen (ply_boot_splash_plugin_t *plugin,
+ plugin);
+
+ if (boot_buffer) {
+- size = ply_buffer_get_size (boot_buffer);
++ plugin->boot_buffer = boot_buffer;
+
++ size = ply_buffer_get_size (boot_buffer);
+ write_on_views (plugin, ply_buffer_get_bytes (boot_buffer), size);
+ }
+
+@@ -330,7 +342,7 @@ display_normal (ply_boot_splash_plugin_t *plugin)
+ ply_list_node_t *node;
+
+ if (plugin->state != PLY_BOOT_SPLASH_DISPLAY_NORMAL)
+- write_on_views (plugin, "\r\n", strlen ("\r\n"));
++ write_on_views (plugin, "\n", strlen ("\n"));
+
+ plugin->state = PLY_BOOT_SPLASH_DISPLAY_NORMAL;
+
+@@ -343,7 +355,7 @@ display_normal (ply_boot_splash_plugin_t *plugin)
+ next_node = ply_list_get_next_node (plugin->messages, node);
+
+ write_on_views (plugin, message, strlen (message));
+- write_on_views (plugin, "\r\n", strlen ("\r\n"));
++ write_on_views (plugin, "\n", strlen ("\n"));
+
+ ply_list_remove_node (plugin->messages, node);
+ node = next_node;
+@@ -358,7 +370,7 @@ display_password (ply_boot_splash_plugin_t *plugin,
+ int i;
+
+ if (plugin->state != PLY_BOOT_SPLASH_DISPLAY_PASSWORD_ENTRY)
+- write_on_views (plugin, "\r\n", strlen ("\r\n"));
++ write_on_views (plugin, "\n", strlen ("\n"));
+ else
+ write_on_views (plugin,
+ CLEAR_LINE_SEQUENCE,
+@@ -387,7 +399,7 @@ display_question (ply_boot_splash_plugin_t *plugin,
+ const char *entry_text)
+ {
+ if (plugin->state != PLY_BOOT_SPLASH_DISPLAY_QUESTION_ENTRY)
+- write_on_views (plugin, "\r\n", strlen ("\r\n"));
++ write_on_views (plugin, "\n", strlen ("\n"));
+ else
+ write_on_views (plugin,
+ CLEAR_LINE_SEQUENCE,
+@@ -407,7 +419,7 @@ display_message (ply_boot_splash_plugin_t *plugin,
+ {
+ if (plugin->state == PLY_BOOT_SPLASH_DISPLAY_NORMAL) {
+ write_on_views (plugin, message, strlen (message));
+- write_on_views (plugin, "\r\n", strlen ("\r\n"));
++ write_on_views (plugin, "\n", strlen ("\n"));
+ } else {
+ ply_list_append_data (plugin->messages, strdup (message));
+ }
+diff --git a/src/plugins/splash/fade-throbber/plugin.c b/src/plugins/splash/fade-throbber/plugin.c
+index 1643b87..a7ab4ef 100644
+--- a/src/plugins/splash/fade-throbber/plugin.c
++++ b/src/plugins/splash/fade-throbber/plugin.c
+@@ -104,6 +104,7 @@ struct _ply_boot_splash_plugin
+ double now;
+
+ uint32_t is_animating : 1;
++ uint32_t is_visible : 1;
+ };
+
+ ply_boot_splash_plugin_interface_t *ply_boot_splash_plugin_get_interface (void);
+@@ -264,6 +265,8 @@ view_free (view_t *view)
+ ply_label_free (view->message_label);
+ free_stars (view);
+
++ ply_pixel_display_set_draw_handler (view->display, NULL, NULL);
++
+ free (view);
+ }
+
+@@ -293,8 +296,12 @@ load_views (ply_boot_splash_plugin_t *plugin)
+ view = ply_list_node_get_data (node);
+ next_node = ply_list_get_next_node (plugin->views, node);
+
+- if (view_load (view))
++ if (view_load (view)) {
+ view_loaded = true;
++ } else {
++ ply_list_remove_node (plugin->views, node);
++ view_free (view);
++ }
+
+ node = next_node;
+ }
+@@ -745,7 +752,14 @@ add_pixel_display (ply_boot_splash_plugin_t *plugin,
+ (ply_pixel_display_draw_handler_t)
+ on_draw, view);
+
+- ply_list_append_data (plugin->views, view);
++ if (plugin->is_visible) {
++ if (view_load (view))
++ ply_list_append_data (plugin->views, view);
++ else
++ view_free (view);
++ } else {
++ ply_list_append_data (plugin->views, view);
++ }
+ }
+
+ static void
+@@ -763,7 +777,6 @@ remove_pixel_display (ply_boot_splash_plugin_t *plugin,
+ next_node = ply_list_get_next_node (plugin->views, node);
+
+ if (view->display == display) {
+- ply_pixel_display_set_draw_handler (view->display, NULL, NULL);
+ view_free (view);
+ ply_list_remove_node (plugin->views, node);
+ return;
+@@ -806,6 +819,8 @@ show_splash_screen (ply_boot_splash_plugin_t *plugin,
+ return false;
+ }
+
++ plugin->is_visible = true;
++
+ ply_trace ("starting boot animation");
+ start_animation (plugin);
+
+@@ -940,6 +955,8 @@ hide_splash_screen (ply_boot_splash_plugin_t *plugin,
+ {
+ assert (plugin != NULL);
+
++ plugin->is_visible = false;
++
+ if (plugin->loop != NULL) {
+ stop_animation (plugin);
+
+diff --git a/src/plugins/splash/script/script-lib-sprite.c b/src/plugins/splash/script/script-lib-sprite.c
+index f292d90..c49297d 100644
+--- a/src/plugins/splash/script/script-lib-sprite.c
++++ b/src/plugins/splash/script/script-lib-sprite.c
+@@ -416,6 +416,22 @@ static script_return_t sprite_window_set_background_bottom_color (script_state_t
+ return script_return_obj_null ();
+ }
+
++static void script_lib_draw_brackground (ply_pixel_buffer_t *pixel_buffer,
++ ply_rectangle_t *clip_area,
++ script_lib_sprite_data_t *data)
++{
++ if (data->background_color_start == data->background_color_end) {
++ ply_pixel_buffer_fill_with_hex_color (pixel_buffer,
++ clip_area,
++ data->background_color_start);
++ } else {
++ ply_pixel_buffer_fill_with_gradient (pixel_buffer,
++ clip_area,
++ data->background_color_start,
++ data->background_color_end);
++ }
++}
++
+ static void script_lib_sprite_draw_area (script_lib_display_t *display,
+ ply_pixel_buffer_t *pixel_buffer,
+ int x,
+@@ -425,6 +441,7 @@ static void script_lib_sprite_draw_area (script_lib_display_t *display,
+ {
+ ply_rectangle_t clip_area;
+ ply_list_node_t *node;
++ sprite_t *sprite;
+ script_lib_sprite_data_t *data = display->data;
+
+ clip_area.x = x;
+@@ -432,22 +449,33 @@ static void script_lib_sprite_draw_area (script_lib_display_t *display,
+ clip_area.width = width;
+ clip_area.height = height;
+
+- if (data->background_color_start == data->background_color_end) {
+- ply_pixel_buffer_fill_with_hex_color (pixel_buffer,
+- &clip_area,
+- data->background_color_start);
++
++ node = ply_list_get_first_node (data->sprite_list);
++ sprite = ply_list_node_get_data (node);
++
++ /* Check If the first sprite should be rendered opaque */
++ if (sprite->image && !sprite->remove_me &&
++ ply_pixel_buffer_is_opaque (sprite->image) && sprite->opacity == 1.0) {
++ int position_x = sprite->x - display->x;
++ int position_y = sprite->y - display->y;
++
++ /* In that case only draw the background if the sprite doesn't
++ * cover the complete area */
++ if (position_x > x || position_y > y ||
++ (ply_pixel_buffer_get_width (sprite->image) + position_x) < (x + width) ||
++ (ply_pixel_buffer_get_height (sprite->image) + position_y) < (y + height))
++ script_lib_draw_brackground (pixel_buffer, &clip_area, data);
+ } else {
+- ply_pixel_buffer_fill_with_gradient (pixel_buffer,
+- &clip_area,
+- data->background_color_start,
+- data->background_color_end);
++ script_lib_draw_brackground (pixel_buffer, &clip_area, data);
+ }
++
+ for (node = ply_list_get_first_node (data->sprite_list);
+ node;
+ node = ply_list_get_next_node (data->sprite_list, node)) {
+- sprite_t *sprite = ply_list_node_get_data (node);
+ int position_x, position_y;
+
++ sprite = ply_list_node_get_data (node);
++
+ if (!sprite->image) continue;
+ if (sprite->remove_me) continue;
+ if (sprite->opacity < 0.011) continue;
+diff --git a/src/plugins/splash/space-flares/plugin.c b/src/plugins/splash/space-flares/plugin.c
+index 5212c38..5af99c1 100644
+--- a/src/plugins/splash/space-flares/plugin.c
++++ b/src/plugins/splash/space-flares/plugin.c
+@@ -1387,7 +1387,14 @@ add_pixel_display (ply_boot_splash_plugin_t *plugin,
+ (ply_pixel_display_draw_handler_t)
+ on_draw, view);
+
+- ply_list_append_data (plugin->views, view);
++ if (plugin->is_visible) {
++ if (view_load (view))
++ ply_list_append_data (plugin->views, view);
++ else
++ view_free (view);
++ } else {
++ ply_list_append_data (plugin->views, view);
++ }
+ }
+
+ static void
+diff --git a/src/plugins/splash/text/plugin.c b/src/plugins/splash/text/plugin.c
+index 468bb89..8260daa 100644
+--- a/src/plugins/splash/text/plugin.c
++++ b/src/plugins/splash/text/plugin.c
+@@ -472,6 +472,9 @@ add_text_display (ply_boot_splash_plugin_t *plugin,
+ on_draw, view);
+
+ ply_list_append_data (plugin->views, view);
++
++ if (plugin->is_animating)
++ view_start_animation (view);
+ }
+
+ static void
+@@ -553,7 +556,9 @@ on_boot_progress (ply_boot_splash_plugin_t *plugin,
+ next_node = ply_list_get_next_node (plugin->views, node);
+
+ ply_text_step_bar_set_percent_done (view->step_bar, percent_done);
+- ply_text_step_bar_draw (view->step_bar);
++
++ if (plugin->is_animating)
++ ply_text_step_bar_draw (view->step_bar);
+
+ node = next_node;
+ }
+diff --git a/src/plugins/splash/throbgress/plugin.c b/src/plugins/splash/throbgress/plugin.c
+index c105e02..a1c8a90 100644
+--- a/src/plugins/splash/throbgress/plugin.c
++++ b/src/plugins/splash/throbgress/plugin.c
+@@ -625,7 +625,14 @@ add_pixel_display (ply_boot_splash_plugin_t *plugin,
+ (ply_pixel_display_draw_handler_t)
+ on_draw, view);
+
+- ply_list_append_data (plugin->views, view);
++ if (plugin->is_visible) {
++ if (view_load (view))
++ ply_list_append_data (plugin->views, view);
++ else
++ view_free (view);
++ } else {
++ ply_list_append_data (plugin->views, view);
++ }
+ }
+
+ static void
+diff --git a/src/plugins/splash/tribar/plugin.c b/src/plugins/splash/tribar/plugin.c
+index 0dcd7f8..ecc17ea 100644
+--- a/src/plugins/splash/tribar/plugin.c
++++ b/src/plugins/splash/tribar/plugin.c
+@@ -478,6 +478,9 @@ add_text_display (ply_boot_splash_plugin_t *plugin,
+ on_draw, view);
+
+ ply_list_append_data (plugin->views, view);
++
++ if (plugin->is_animating)
++ view_start_animation (view);
+ }
+
+ static void
+@@ -559,7 +562,9 @@ on_boot_progress (ply_boot_splash_plugin_t *plugin,
+ next_node = ply_list_get_next_node (plugin->views, node);
+
+ ply_text_progress_bar_set_percent_done (view->progress_bar, percent_done);
+- ply_text_progress_bar_draw (view->progress_bar);
++
++ if (plugin->is_animating)
++ ply_text_progress_bar_draw (view->progress_bar);
+
+ node = next_node;
+ }
+diff --git a/src/plugins/splash/two-step/plugin.c b/src/plugins/splash/two-step/plugin.c
+index 0899ace..070741d 100644
+--- a/src/plugins/splash/two-step/plugin.c
++++ b/src/plugins/splash/two-step/plugin.c
+@@ -267,11 +267,15 @@ view_load (view_t *view)
+
+ view_load_end_animation (view);
+
+- ply_trace ("loading progress animation");
+- if (!ply_progress_animation_load (view->progress_animation)) {
+- ply_trace ("optional progress animation wouldn't load");
+- ply_progress_animation_free (view->progress_animation);
+- view->progress_animation = NULL;
++ if (view->progress_animation != NULL) {
++ ply_trace ("loading progress animation");
++ if (!ply_progress_animation_load (view->progress_animation)) {
++ ply_trace ("optional progress animation wouldn't load");
++ ply_progress_animation_free (view->progress_animation);
++ view->progress_animation = NULL;
++ }
++ } else {
++ ply_trace ("this theme has no progress animation");
+ }
+
+ if (view->throbber != NULL) {
+@@ -875,7 +879,12 @@ draw_background (view_t *view,
+ if (view->background_image != NULL) {
+ uint32_t *data;
+ data = ply_image_get_data (view->background_image);
+- ply_pixel_buffer_fill_with_argb32_data (pixel_buffer, &area, data);
++
++ /* We must pass NULL as fill area, because the fill area
++ must be sized as the image we're sourcing from, otherwise
++ sampling does not work
++ */
++ ply_pixel_buffer_fill_with_argb32_data_with_clip (pixel_buffer, NULL, NULL, data);
+ }
+
+ if (plugin->watermark_image != NULL) {
+@@ -986,7 +995,14 @@ add_pixel_display (ply_boot_splash_plugin_t *plugin,
+ ply_pixel_display_set_draw_handler (view->display,
+ (ply_pixel_display_draw_handler_t)
+ on_draw, view);
+- ply_list_append_data (plugin->views, view);
++ if (plugin->is_visible) {
++ if (view_load (view))
++ ply_list_append_data (plugin->views, view);
++ else
++ view_free (view);
++ } else {
++ ply_list_append_data (plugin->views, view);
++ }
+ }
+
+ static void
+@@ -1329,8 +1345,8 @@ system_update (ply_boot_splash_plugin_t *plugin,
+
+ view = ply_list_node_get_data (node);
+ next_node = ply_list_get_next_node (plugin->views, node);
+- ply_progress_animation_set_percent_done (view->progress_animation,
+- (double) progress / 100.f);
++ if (view->progress_animation != NULL)
++ ply_progress_animation_set_percent_done (view->progress_animation, (double) progress / 100.f);
+ node = next_node;
+ }
+ }
+diff --git a/src/plymouthd.defaults b/src/plymouthd.defaults
+index fc48b15..4d3b6d4 100644
+--- a/src/plymouthd.defaults
++++ b/src/plymouthd.defaults
+@@ -3,3 +3,4 @@
+ [Daemon]
+ Theme=spinner
+ ShowDelay=5
++DeviceTimeout=5
+diff --git a/systemd-units/Makefile.am b/systemd-units/Makefile.am
+index 89355ac..b1d843b 100644
+--- a/systemd-units/Makefile.am
++++ b/systemd-units/Makefile.am
+@@ -50,21 +50,20 @@ install-data-hook:
+ $(LN_S) ../plymouth-halt.service)
+
+ uninstall-hook:
+- rm -f \
+ (cd $(DESTDIR)$(SYSTEMD_UNIT_DIR)/initrd-switch-root.target.wants && \
+- rm -f plymouth-start.service plymouth-switch-root.service) \
++ rm -f plymouth-start.service plymouth-switch-root.service)
+ (cd $(DESTDIR)$(SYSTEMD_UNIT_DIR)/sysinit.target.wants && \
+- rm -f plymouth-start.service plymouth-read-write.service) \
++ rm -f plymouth-start.service plymouth-read-write.service)
+ (cd $(DESTDIR)$(SYSTEMD_UNIT_DIR)/multi-user.target.wants && \
+- rm -f plymouth-quit.service plymouth-quit-wait.service) \
++ rm -f plymouth-quit.service plymouth-quit-wait.service)
+ (cd $(DESTDIR)$(SYSTEMD_UNIT_DIR)/reboot.target.wants && \
+- rm -f plymouth-reboot.service) \
++ rm -f plymouth-reboot.service)
+ (cd $(DESTDIR)$(SYSTEMD_UNIT_DIR)/kexec.target.wants && \
+- rm -f plymouth-kexec.service) \
++ rm -f plymouth-kexec.service)
+ (cd $(DESTDIR)$(SYSTEMD_UNIT_DIR)/poweroff.target.wants && \
+- rm -f plymouth-poweroff.service) \
++ rm -f plymouth-poweroff.service)
+ (cd $(DESTDIR)$(SYSTEMD_UNIT_DIR)/halt.target.wants && \
+- rm -f plymouth-halt.service) \
++ rm -f plymouth-halt.service)
+ rmdir --ignore-fail-on-non-empty \
+ $(DESTDIR)$(SYSTEMD_UNIT_DIR)/sysinit.target.wants \
+ $(DESTDIR)$(SYSTEMD_UNIT_DIR)/multi-user.target.wants \
+diff --git a/themes/script/script.script b/themes/script/script.script
+index 7ea9d5e..25a2f2b 100644
+--- a/themes/script/script.script
++++ b/themes/script/script.script
+@@ -152,27 +152,19 @@ Plymouth.SetQuitFunction(quit_callback);
+
+ #----------------------------------------- Message --------------------------------
+
+-message_sprites = [];
+-message_sprite_count = 0;
+-message_sprite_y = 10;
++message_sprite = Sprite();
++message_sprite.SetPosition(10, 10, 10000);
+
+ fun display_message_callback (text)
+ {
+ my_image = Image.Text(text, 1, 1, 1);
+- message_sprites[message_sprite_count] = Sprite(my_image);
+- message_sprites[message_sprite_count].SetPosition(10, message_sprite_y, 10000);
+- message_sprites[message_sprite_count].text = text;
+- message_sprite_count++;
+- message_sprite_y += my_image.GetHeight();
++ message_sprite.SetImage(my_image);
+ }
+
+ fun hide_message_callback (text)
+ {
+- for (i = 0; i < message_sprite_count; i++)
+- {
+- if (message_sprites[i].text == text)
+- message_sprites[i] = NULL;
+- }
++ message_sprite = Sprite();
++ message_sprite.SetPosition(10, 10, 10000);
+ }
+
+ Plymouth.SetDisplayMessageFunction (display_message_callback);