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);
