]> git.ipfire.org Git - thirdparty/plymouth.git/commitdiff
[drm] Add start of a drm plugin
authorRay Strode <rstrode@redhat.com>
Wed, 16 Sep 2009 22:27:53 +0000 (18:27 -0400)
committerRay Strode <rstrode@redhat.com>
Mon, 28 Sep 2009 21:55:19 +0000 (17:55 -0400)
This commit adds most of the non-driver specific bits
necessary for backing a renderer plugin.

Subsequent commits will add the driver specific bits.

configure.ac
scripts/plymouth-populate-initrd.in
src/libplybootsplash/ply-renderer.c
src/plugins/renderers/Makefile.am
src/plugins/renderers/drm/Makefile.am [new file with mode: 0644]
src/plugins/renderers/drm/plugin.c [new file with mode: 0644]
src/plugins/renderers/drm/ply-renderer-driver.h [new file with mode: 0644]

index df076e2c3ebcfdeaaeb8310316285793f4ff4c18..0758dcad16723c96ecedca8c3875d003d168cfb3 100644 (file)
@@ -56,6 +56,10 @@ PKG_CHECK_MODULES(GTK, [gtk+-2.0 >= 2.12.0 ])
 AC_SUBST(GTK_CFLAGS)
 AC_SUBST(GTK_LIBS)
 
+PKG_CHECK_MODULES(DRM, [libdrm])
+AC_SUBST(DRM_CFLAGS)
+AC_SUBST(DRM_LIBS)
+
 AC_ARG_ENABLE(tracing, AS_HELP_STRING([--enable-tracing],[enable verbose tracing code]),enable_tracing=$enableval,enable_tracing=yes)
 
 if test x$enable_tracing = xyes; then
@@ -211,6 +215,7 @@ AC_OUTPUT([Makefile
            src/plugins/Makefile
            src/plugins/renderers/Makefile
            src/plugins/renderers/frame-buffer/Makefile
+           src/plugins/renderers/drm/Makefile
            src/plugins/splash/Makefile
            src/plugins/splash/throbgress/Makefile
            src/plugins/splash/fade-throbber/Makefile
index 42b119c3575e8db7dd6bb9a392dafae06b13958d..14eb6fc71c9aeed7307f782a4784d6b4107c2493 100755 (executable)
@@ -93,6 +93,7 @@ fi
 
 inst ${PLYMOUTH_PLUGIN_PATH}/${PLYMOUTH_MODULE_NAME}.so $INITRDDIR
 
+inst ${PLYMOUTH_PLUGIN_PATH}/renderers/drm.so $INITRDDIR
 inst ${PLYMOUTH_PLUGIN_PATH}/renderers/frame-buffer.so $INITRDDIR
 
 if [ -d ${DATADIR}/plymouth/themes/${PLYMOUTH_THEME_NAME} ]; then
index 9320030fb9117acdcbde0c83d3e710584b430093..5410feb83921e052d8256cf1f2d189504f4ca2f5 100644 (file)
@@ -220,6 +220,7 @@ ply_renderer_open (ply_renderer_t *renderer)
    */
   const char *known_plugins[] =
     {
+      PLYMOUTH_PLUGIN_PATH "renderers/drm.so",
       PLYMOUTH_PLUGIN_PATH "renderers/frame-buffer.so",
       NULL
     };
index 2a9e8a0c43408c990fbc9066cd83a88a78b84d77..2fadbf4070bfca4004d4acc347d0aa2d9038e321 100644 (file)
@@ -1,2 +1,2 @@
-SUBDIRS = frame-buffer
+SUBDIRS = frame-buffer drm
 MAINTAINERCLEANFILES = Makefile.in
diff --git a/src/plugins/renderers/drm/Makefile.am b/src/plugins/renderers/drm/Makefile.am
new file mode 100644 (file)
index 0000000..328b881
--- /dev/null
@@ -0,0 +1,21 @@
+INCLUDES = -I$(top_srcdir)                                                    \
+           -I$(srcdir)/../../../libply                                        \
+           -I$(srcdir)/../../../libplybootsplash                              \
+           -I$(srcdir)/../../..                                               \
+           -I$(srcdir)/../..                                                  \
+           -I$(srcdir)/..                                                     \
+           -I$(srcdir)
+
+plugindir = $(libdir)/plymouth/renderers
+plugin_LTLIBRARIES = drm.la
+
+drm_la_CFLAGS = $(PLYMOUTH_CFLAGS) $(DRM_CFLAGS)
+
+drm_la_LDFLAGS = -module -avoid-version -export-dynamic
+drm_la_LIBADD = $(PLYMOUTH_LIBS) $(DRM_LIBS)                                  \
+                         ../../../libply/libply.la                            \
+                         ../../../libplybootsplash/libplybootsplash.la
+drm_la_SOURCES = $(srcdir)/plugin.c                                           \
+                $(srcdir)/ply-renderer-driver.h
+
+MAINTAINERCLEANFILES = Makefile.in
diff --git a/src/plugins/renderers/drm/plugin.c b/src/plugins/renderers/drm/plugin.c
new file mode 100644 (file)
index 0000000..67a6bea
--- /dev/null
@@ -0,0 +1,1049 @@
+/* plugin.c - drm backend renderer plugin
+ *
+ * Copyright (C) 2006-2009 Red Hat, Inc.
+ *               2008 Charlie Brej <cbrej@cs.man.ac.uk>
+ *
+ * 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: Charlie Brej <cbrej@cs.man.ac.uk>
+ *             Kristian Høgsberg <krh@redhat.com>
+ *             Peter Jones <pjones@redhat.com>
+ *             Ray Strode <rstrode@redhat.com>
+ */
+#include "config.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/drm.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include "ply-buffer.h"
+#include "ply-event-loop.h"
+#include "ply-list.h"
+#include "ply-logger.h"
+#include "ply-rectangle.h"
+#include "ply-region.h"
+#include "ply-terminal.h"
+
+#include "ply-renderer.h"
+#include "ply-renderer-plugin.h"
+#include "ply-renderer-driver.h"
+
+#define BYTES_PER_PIXEL (4)
+
+struct _ply_renderer_head
+{
+  ply_renderer_backend_t *backend;
+  ply_pixel_buffer_t *pixel_buffer;
+  ply_rectangle_t area;
+
+  unsigned long row_stride;
+
+  drmModeConnector *connector;
+  drmModeModeInfo *mode;
+
+  uint32_t controller_id;
+  uint32_t encoder_id;
+  uint32_t console_buffer_id;
+  uint32_t scan_out_buffer_id;
+};
+
+struct _ply_renderer_input_source
+{
+  ply_fd_watch_t *terminal_input_watch;
+
+  ply_buffer_t   *key_buffer;
+
+  ply_renderer_input_source_handler_t handler;
+  void           *user_data;
+};
+
+struct _ply_renderer_backend
+{
+  ply_event_loop_t *loop;
+  ply_console_t *console;
+  ply_terminal_t *terminal;
+
+  ply_renderer_driver_interface_t *driver_interface;
+  ply_renderer_driver_t *driver;
+
+  int device_fd;
+  char *device_name;
+  drmModeRes *resources;
+
+  ply_renderer_input_source_t input_source;
+  ply_list_t *heads;
+
+  int32_t dither_red;
+  int32_t dither_green;
+  int32_t dither_blue;
+};
+
+ply_renderer_plugin_interface_t *ply_renderer_backend_get_interface (void);
+static void ply_renderer_head_redraw (ply_renderer_backend_t *backend,
+                                      ply_renderer_head_t    *head);
+
+static ply_renderer_head_t *
+ply_renderer_head_new (ply_renderer_backend_t *backend,
+                       drmModeConnector       *connector,
+                       uint32_t                encoder_id,
+                       uint32_t                controller_id,
+                       uint32_t                console_buffer_id,
+                       drmModeModeInfo        *mode)
+{
+  ply_renderer_head_t *head;
+
+  head = calloc (1, sizeof (ply_renderer_head_t));
+
+  head->backend = backend;
+  head->connector = connector;
+  head->encoder_id = encoder_id;
+  head->controller_id = controller_id;
+  head->console_buffer_id = console_buffer_id;
+  head->mode = mode;
+
+  head->area.x = 0;
+  head->area.y = 0;
+  head->area.width = mode->hdisplay;
+  head->area.height = mode->vdisplay;
+
+  head->pixel_buffer = ply_pixel_buffer_new (head->area.width, head->area.height);
+
+  ply_pixel_buffer_fill_with_color (head->pixel_buffer, NULL,
+                                    0.0, 0.0, 0.0, 1.0);
+
+  return head;
+}
+
+static void
+ply_renderer_head_free (ply_renderer_head_t *head)
+{
+  ply_pixel_buffer_free (head->pixel_buffer);
+  drmModeFreeConnector (head->connector);
+  free (head);
+}
+
+static bool
+ply_renderer_head_set_scan_out_buffer (ply_renderer_backend_t *backend,
+                                       ply_renderer_head_t    *head,
+                                       uint32_t                buffer_id)
+{
+
+  /* Tell the controller to use the allocated scan out buffer
+   */
+  if (drmModeSetCrtc (backend->device_fd, head->controller_id, buffer_id,
+                      0, 0, &head->connector->connector_id, 1, head->mode) < 0)
+    return false;
+
+  return true;
+}
+
+static bool
+ply_renderer_head_map (ply_renderer_backend_t *backend,
+                       ply_renderer_head_t    *head)
+{
+  assert (backend != NULL);
+  assert (backend->device_fd >= 0);
+  assert (backend->driver_interface != NULL);
+  assert (backend->driver != NULL);
+
+  assert (head != NULL);
+
+  head->scan_out_buffer_id =
+    backend->driver_interface->create_buffer (backend->driver,
+                                              head->area.width, head->area.height,
+                                              &head->row_stride);
+
+  if (head->scan_out_buffer_id == 0)
+    return false;
+
+  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);
+      head->scan_out_buffer_id = 0;
+      return false;
+    }
+
+  /* FIXME: Maybe we should blit the fbcon contents instead of the (blank)
+   * shadow buffer?
+   */
+  ply_renderer_head_redraw (backend, head);
+
+  if (!ply_renderer_head_set_scan_out_buffer (backend, head,
+                                              head->scan_out_buffer_id))
+    {
+      backend->driver_interface->destroy_buffer (backend->driver,
+                                                 head->scan_out_buffer_id);
+      head->scan_out_buffer_id = 0;
+      return false;
+    }
+
+  return true;
+}
+
+static void
+ply_renderer_head_unmap (ply_renderer_backend_t *backend,
+                         ply_renderer_head_t    *head)
+{
+  backend->driver_interface->unmap_buffer (backend->driver,
+                                           head->scan_out_buffer_id);
+
+  backend->driver_interface->destroy_buffer (backend->driver,
+                                             head->scan_out_buffer_id);
+  head->scan_out_buffer_id = 0;
+}
+
+static void
+flush_area (const char      *src,
+            unsigned long    src_row_stride,
+            char            *dst,
+            unsigned long    dst_row_stride,
+            ply_rectangle_t *area_to_flush)
+{
+  unsigned long x1, y1, x2, y2, y;
+
+  x1 = area_to_flush->x;
+  y1 = area_to_flush->y;
+  x2 = x1 + area_to_flush->width;
+  y2 = y1 + area_to_flush->height;
+
+  if (area_to_flush->width * 4 == src_row_stride &&
+      area_to_flush->width * 4 == dst_row_stride)
+    {
+      memcpy (dst, src, area_to_flush->width * area_to_flush->height * 4);
+      return;
+    }
+
+  for (y = y1; y < y2; y++)
+    {
+      memcpy (dst, src, area_to_flush->width * 4);
+      dst += dst_row_stride;
+      src += src_row_stride;
+    }
+}
+
+static void
+ply_renderer_head_flush_area (ply_renderer_head_t *head,
+                              ply_rectangle_t     *area_to_flush,
+                              char                *map_address)
+{
+  uint32_t *shadow_buffer;
+  char *dst, *src;
+
+  shadow_buffer = ply_pixel_buffer_get_argb32_data (head->pixel_buffer);
+
+  dst = &map_address[area_to_flush->y * head->row_stride + area_to_flush->x * BYTES_PER_PIXEL];
+  src = (char *) &shadow_buffer[area_to_flush->y * head->area.width + area_to_flush->x];
+
+  flush_area (src, head->area.width * 4, dst, head->row_stride, area_to_flush);
+}
+
+static void
+free_heads (ply_renderer_backend_t *backend)
+{
+  ply_list_node_t *node;
+
+  node = ply_list_get_first_node (backend->heads);
+  while (node != NULL)
+    {
+      ply_list_node_t *next_node;
+      ply_renderer_head_t *head;
+
+      head = (ply_renderer_head_t *) ply_list_node_get_data (node);
+      next_node = ply_list_get_next_node (backend->heads, node);
+
+      ply_renderer_head_free (head);
+      ply_list_remove_node (backend->heads, node);
+
+      node = next_node;
+    }
+}
+
+static ply_renderer_backend_t *
+create_backend (const char *device_name,
+                ply_terminal_t *terminal,
+                ply_console_t *console)
+{
+  ply_renderer_backend_t *backend;
+
+  backend = calloc (1, sizeof (ply_renderer_backend_t));
+
+  if (device_name != NULL)
+    backend->device_name = strdup (device_name);
+  else
+    backend->device_name = strdup ("/dev/dri/card0");
+
+  backend->device_fd = -1;
+
+  backend->loop = ply_event_loop_get_default ();
+  backend->heads = ply_list_new ();
+  backend->input_source.key_buffer = ply_buffer_new ();
+  backend->console = console;
+  backend->terminal = terminal;
+
+  return backend;
+}
+
+static void
+destroy_backend (ply_renderer_backend_t *backend)
+{
+  free_heads (backend);
+  ply_list_free (backend->heads);
+
+  free (backend->device_name);
+
+  free (backend);
+}
+
+static char *
+find_driver_for_device (const char *device_name)
+{
+  char *driver;
+  int major_number, minor_number;
+  struct stat file_attributes;
+  char *device_path;
+  char device_link_path[PATH_MAX + 1] = "";
+
+  if (stat (device_name, &file_attributes) < 0)
+    return NULL;
+
+  if (!S_ISCHR (file_attributes.st_mode))
+    return NULL;
+
+  major_number = major (file_attributes.st_rdev);
+  minor_number = minor (file_attributes.st_rdev);
+
+  asprintf (&device_path, "/sys/dev/char/%d:%d/device/driver",
+            major_number, minor_number);
+
+  if (readlink (device_path, device_link_path, sizeof (device_link_path) - 1) < 0)
+    {
+      free (device_path);
+      return NULL;
+    }
+  free (device_path);
+
+  driver = strrchr (device_link_path, '/');
+
+  if (driver == NULL)
+    return NULL;
+
+  return strdup (driver + strlen ("/"));
+}
+
+static void
+on_active_vt_changed (ply_renderer_backend_t *backend)
+{
+  ply_list_node_t *node;
+
+  if (ply_console_get_active_vt (backend->console) !=
+      ply_terminal_get_vt_number (backend->terminal))
+    return;
+
+  node = ply_list_get_first_node (backend->heads);
+  while (node != NULL)
+    {
+      ply_list_node_t *next_node;
+      ply_renderer_head_t *head;
+
+      head = (ply_renderer_head_t *) ply_list_node_get_data (node);
+      next_node = ply_list_get_next_node (backend->heads, node);
+
+      if (head->scan_out_buffer_id != 0)
+        ply_renderer_head_set_scan_out_buffer (backend, head,
+                                               head->scan_out_buffer_id);
+
+      node = next_node;
+    }
+}
+
+static bool
+load_driver (ply_renderer_backend_t *backend)
+{
+  char *driver_name;
+  int device_fd;
+
+  driver_name = find_driver_for_device (backend->device_name);
+  device_fd = drmOpen (driver_name, NULL);
+
+  if (device_fd < 0)
+    {
+      free (driver_name);
+      return false;
+    }
+
+#if 0
+  Something like...
+  if (strcmp (driver_name, "i915") == 0)
+    {
+      backend->driver_interface = ply_renderer_i915_driver_get_interface ();
+    }
+#endif
+  free (driver_name);
+
+  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;
+
+  return true;
+}
+
+static void
+unload_driver (ply_renderer_backend_t *backend)
+{
+  if (backend->driver == NULL)
+    return;
+
+  assert (backend->driver_interface != NULL);
+
+  backend->driver_interface->destroy_driver (backend->driver);
+  backend->driver = NULL;
+
+  backend->driver_interface = NULL;
+
+  if (backend->device_fd >= 0)
+    {
+      drmClose (backend->device_fd);
+      backend->device_fd = -1;
+    }
+}
+
+static bool
+open_device (ply_renderer_backend_t *backend)
+{
+  assert (backend != NULL);
+  assert (backend->device_name != NULL);
+
+  if (!load_driver (backend))
+    return false;
+
+  ply_console_watch_for_active_vt_change (backend->console,
+                                          (ply_console_active_vt_changed_handler_t)
+                                          on_active_vt_changed,
+                                          backend);
+
+  return true;
+}
+
+static void
+close_device (ply_renderer_backend_t *backend)
+{
+  ply_terminal_close (backend->terminal);
+  free_heads (backend);
+
+  ply_console_stop_watching_for_active_vt_change (backend->console,
+                                                  (ply_console_active_vt_changed_handler_t)
+                                                  on_active_vt_changed,
+                                                  backend);
+
+  unload_driver (backend);
+}
+
+static drmModeModeInfo *
+get_active_mode_for_connector (ply_renderer_backend_t *backend,
+                               drmModeConnector       *connector)
+{
+  return &connector->modes[0];
+}
+
+static bool
+controller_is_available (ply_renderer_backend_t *backend,
+                         uint32_t                controller_id)
+{
+  ply_list_node_t *node;
+
+  node = ply_list_get_first_node (backend->heads);
+  while (node != NULL)
+    {
+      ply_list_node_t *next_node;
+      ply_renderer_head_t *head;
+
+      head = (ply_renderer_head_t *) ply_list_node_get_data (node);
+      next_node = ply_list_get_next_node (backend->heads, node);
+
+      if (head->controller_id == controller_id)
+        return false;
+
+      node = next_node;
+    }
+
+  return true;
+}
+
+static uint32_t
+find_controller_for_encoder (ply_renderer_backend_t *backend,
+                             drmModeEncoder         *encoder)
+{
+  int i;
+  uint32_t possible_crtcs;
+
+  /* Monitor is already lit. We'll use the same controller.
+   */
+  if (encoder->crtc_id != 0)
+    return encoder->crtc_id;
+
+  /* Monitor cable is plugged in, but the monitor isn't lit
+   * yet. Let's pick an available controller and light it up
+   * ourselves.
+   */
+  for (i = 0,
+       possible_crtcs = encoder->possible_crtcs;
+       possible_crtcs != 0x0;
+       i++, possible_crtcs >>= 1)
+    {
+      /* controller isn't compatible with encoder
+       */
+      if ((possible_crtcs & 0x1) == 0)
+        continue;
+
+      /* controller is already being used
+       */
+      if (!controller_is_available (backend, backend->resources->crtcs[i]))
+        continue;
+
+      assert (i < backend->resources->count_crtcs);
+      return backend->resources->crtcs[i];
+    }
+
+  return 0;
+}
+
+static bool
+encoder_is_available (ply_renderer_backend_t *backend,
+                      uint32_t                encoder_id)
+{
+  ply_list_node_t *node;
+
+  node = ply_list_get_first_node (backend->heads);
+  while (node != NULL)
+    {
+      ply_list_node_t *next_node;
+      ply_renderer_head_t *head;
+
+      head = (ply_renderer_head_t *) ply_list_node_get_data (node);
+      next_node = ply_list_get_next_node (backend->heads, node);
+
+      if (head->encoder_id == encoder_id)
+        return false;
+
+      node = next_node;
+    }
+
+  return true;
+}
+
+static drmModeEncoder *
+find_unused_encoder_for_connector (ply_renderer_backend_t *backend,
+                                   drmModeConnector       *connector)
+{
+  int i;
+  drmModeEncoder *encoder;
+
+  for (i = 0; i < connector->count_encoders; i++)
+    {
+      encoder = drmModeGetEncoder (backend->device_fd,
+                                   connector->encoders[i]);
+
+      if (encoder == NULL)
+        continue;
+
+      if (encoder_is_available (backend, encoder->encoder_id))
+        return encoder;
+
+      drmModeFreeEncoder (encoder);
+    }
+
+  return NULL;
+}
+
+static drmModeEncoder *
+find_encoder_for_connector (ply_renderer_backend_t *backend,
+                            drmModeConnector       *connector)
+{
+  int i;
+  drmModeEncoder *encoder;
+
+  assert (backend != NULL);
+
+  for (i = 0; i < connector->count_encoders; i++)
+    {
+      encoder = drmModeGetEncoder (backend->device_fd,
+                                   connector->encoders[i]);
+
+      if (encoder == NULL)
+        continue;
+
+      if (encoder->encoder_id == connector->encoder_id)
+        return encoder;
+
+      drmModeFreeEncoder (encoder);
+    }
+
+  /* No encoder yet, pick one
+   */
+  return find_unused_encoder_for_connector (backend, connector);
+}
+
+static uint32_t
+get_console_buffer_id (ply_renderer_backend_t *backend,
+                       uint32_t                controller_id)
+{
+  drmModeCrtc *controller;
+  uint32_t console_buffer_id;
+
+  console_buffer_id = 0;
+  controller = drmModeGetCrtc (backend->device_fd, controller_id);
+
+  if (controller == NULL)
+    return 0;
+
+  console_buffer_id = controller->buffer_id;
+
+  drmModeFreeCrtc (controller);
+
+  return console_buffer_id;
+}
+
+static bool
+create_heads_for_active_connectors (ply_renderer_backend_t *backend)
+{
+  int i;
+  drmModeConnector *connector;
+
+  for (i = 0; i < backend->resources->count_connectors; i++)
+    {
+      ply_renderer_head_t *head;
+      drmModeEncoder *encoder;
+      uint32_t controller_id;
+      uint32_t encoder_id;
+      uint32_t console_buffer_id;
+      drmModeModeInfo *mode;
+
+      connector = drmModeGetConnector (backend->device_fd,
+                                       backend->resources->connectors[i]);
+
+      if (connector == NULL)
+        continue;
+
+      if (connector->connection != DRM_MODE_CONNECTED)
+        {
+          drmModeFreeConnector (connector);
+          continue;
+        }
+
+      if (connector->count_modes <= 0)
+        {
+          drmModeFreeConnector (connector);
+          continue;
+        }
+
+      encoder = find_encoder_for_connector (backend, connector);
+
+      if (encoder == NULL)
+        {
+          drmModeFreeConnector (connector);
+          continue;
+        }
+
+      encoder_id = encoder->encoder_id;
+      controller_id = find_controller_for_encoder (backend, encoder);
+      drmModeFreeEncoder (encoder);
+
+      if (controller_id == 0)
+        {
+          drmModeFreeConnector (connector);
+          continue;
+        }
+
+      mode = get_active_mode_for_connector (backend, connector);
+
+      console_buffer_id = get_console_buffer_id (backend, controller_id);
+
+      head = ply_renderer_head_new (backend, connector, encoder_id,
+                                    controller_id, console_buffer_id,
+                                    mode);
+
+      ply_list_append_data (backend->heads, head);
+    }
+
+  return ply_list_get_length (backend->heads) > 0;
+}
+
+static bool
+query_device (ply_renderer_backend_t *backend)
+{
+  assert (backend != NULL);
+  assert (backend->device_fd >= 0);
+
+  backend->resources = drmModeGetResources (backend->device_fd);
+
+  if (backend->resources == NULL)
+    {
+      ply_trace ("Could not get card resources");
+      return false;
+    }
+
+  if (!create_heads_for_active_connectors (backend))
+    {
+      ply_trace ("Could not initialize heads");
+      return false;
+    }
+
+  return true;
+}
+
+static bool
+map_to_device (ply_renderer_backend_t *backend)
+{
+  ply_list_node_t *node;
+  bool head_mapped;
+
+  head_mapped = false;
+  node = ply_list_get_first_node (backend->heads);
+  while (node != NULL)
+    {
+      ply_list_node_t *next_node;
+      ply_renderer_head_t *head;
+
+      head = (ply_renderer_head_t *) ply_list_node_get_data (node);
+      next_node = ply_list_get_next_node (backend->heads, node);
+
+      if (ply_renderer_head_map (backend, head))
+        head_mapped = true;
+
+      node = next_node;
+    }
+
+  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))
+    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;
+    }
+  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);
+    }
+
+  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_renderer_head_set_scan_out_buffer (backend,
+                                         head, 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)
+    {
+      ply_list_node_t *next_node;
+      ply_renderer_head_t *head;
+
+      head = (ply_renderer_head_t *) ply_list_node_get_data (node);
+      next_node = ply_list_get_next_node (backend->heads, node);
+
+      ply_renderer_head_set_scan_out_buffer_to_console (backend, head,
+                                                        should_set_to_black);
+
+      ply_renderer_head_unmap (backend, head);
+
+      node = next_node;
+    }
+}
+
+static void
+reset_scan_out_buffer_if_needed (ply_renderer_backend_t *backend,
+                                 ply_renderer_head_t    *head)
+{
+  drmModeCrtc *controller;
+
+  if (ply_console_get_active_vt (backend->console) !=
+      ply_terminal_get_vt_number (backend->terminal))
+    return;
+
+  controller = drmModeGetCrtc (backend->device_fd, head->controller_id);
+
+  if (controller == NULL)
+    return;
+
+  if (controller->buffer_id != head->scan_out_buffer_id)
+    {
+      ply_trace ("Something stole the monitor");
+      ply_renderer_head_set_scan_out_buffer (backend, head,
+                                             head->scan_out_buffer_id);
+    }
+
+  drmModeFreeCrtc (controller);
+}
+
+static void
+flush_head (ply_renderer_backend_t *backend,
+            ply_renderer_head_t    *head)
+{
+  ply_region_t *updated_region;
+  ply_list_t *areas_to_flush;
+  ply_list_node_t *node;
+  ply_pixel_buffer_t *pixel_buffer;
+  char *map_address;
+
+  assert (backend != NULL);
+
+  ply_console_set_mode (backend->console, PLY_CONSOLE_MODE_GRAPHICS);
+  pixel_buffer = head->pixel_buffer;
+  updated_region = ply_pixel_buffer_get_updated_areas (pixel_buffer);
+  areas_to_flush = ply_region_get_rectangle_list (updated_region);
+
+  map_address =
+    backend->driver_interface->begin_flush (backend->driver,
+                                            head->scan_out_buffer_id);
+
+  node = ply_list_get_first_node (areas_to_flush);
+  while (node != NULL)
+    {
+      ply_list_node_t *next_node;
+      ply_rectangle_t *area_to_flush;
+
+      area_to_flush = (ply_rectangle_t *) ply_list_node_get_data (node);
+
+      next_node = ply_list_get_next_node (areas_to_flush, node);
+
+      reset_scan_out_buffer_if_needed (backend, head);
+      ply_renderer_head_flush_area (head, area_to_flush, map_address);
+
+      node = next_node;
+    }
+
+  backend->driver_interface->end_flush (backend->driver,
+                                        head->scan_out_buffer_id);
+
+  ply_region_clear (updated_region);
+}
+
+static void
+ply_renderer_head_redraw (ply_renderer_backend_t *backend,
+                          ply_renderer_head_t    *head)
+{
+  ply_region_t *region;
+
+  region = ply_pixel_buffer_get_updated_areas (head->pixel_buffer);
+
+  ply_region_add_rectangle (region, &head->area);
+
+  flush_head (backend, head);
+}
+
+static ply_list_t *
+get_heads (ply_renderer_backend_t *backend)
+{
+  return backend->heads;
+}
+
+static ply_pixel_buffer_t *
+get_buffer_for_head (ply_renderer_backend_t *backend,
+                     ply_renderer_head_t    *head)
+{
+
+  if (head->backend != backend)
+    return NULL;
+
+  return head->pixel_buffer;
+}
+
+static bool
+has_input_source (ply_renderer_backend_t      *backend,
+                  ply_renderer_input_source_t *input_source)
+{
+  return input_source == &backend->input_source;
+}
+
+static ply_renderer_input_source_t *
+get_input_source (ply_renderer_backend_t *backend)
+{
+  return &backend->input_source;
+}
+
+static void
+on_key_event (ply_renderer_input_source_t *input_source,
+              int                          terminal_fd)
+{ 
+  ply_buffer_append_from_fd (input_source->key_buffer,
+                             terminal_fd);
+
+  if (input_source->handler != NULL)
+    input_source->handler (input_source->user_data, input_source->key_buffer, input_source);
+
+}
+
+static bool
+open_input_source (ply_renderer_backend_t      *backend,
+                   ply_renderer_input_source_t *input_source)
+{
+  int terminal_fd;
+
+  assert (backend != NULL);
+  assert (has_input_source (backend, input_source));
+
+  terminal_fd = ply_terminal_get_fd (backend->terminal);
+
+  input_source->terminal_input_watch = ply_event_loop_watch_fd (backend->loop, terminal_fd, PLY_EVENT_LOOP_FD_STATUS_HAS_DATA,
+                                                                (ply_event_handler_t) on_key_event,
+                                                                NULL, input_source);
+  return true;
+}
+
+static void
+set_handler_for_input_source (ply_renderer_backend_t      *backend,
+                              ply_renderer_input_source_t *input_source,
+                              ply_renderer_input_source_handler_t handler,
+                              void                        *user_data)
+{
+  assert (backend != NULL);
+  assert (has_input_source (backend, input_source));
+
+  input_source->handler = handler;
+  input_source->user_data = user_data;
+}
+
+static void
+close_input_source (ply_renderer_backend_t      *backend,
+                    ply_renderer_input_source_t *input_source)
+{
+  assert (backend != NULL);
+  assert (has_input_source (backend, input_source));
+
+  ply_event_loop_stop_watching_fd (backend->loop, input_source->terminal_input_watch);
+  input_source->terminal_input_watch = NULL;
+}
+
+ply_renderer_plugin_interface_t *
+ply_renderer_backend_get_interface (void)
+{
+  static ply_renderer_plugin_interface_t plugin_interface =
+    {
+      .create_backend = create_backend,
+      .destroy_backend = destroy_backend,
+      .open_device = open_device,
+      .close_device = close_device,
+      .query_device = query_device,
+      .map_to_device = map_to_device,
+      .unmap_from_device = unmap_from_device,
+      .flush_head = flush_head,
+      .get_heads = get_heads,
+      .get_buffer_for_head = get_buffer_for_head,
+      .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
+    };
+
+  return &plugin_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-driver.h b/src/plugins/renderers/drm/ply-renderer-driver.h
new file mode 100644 (file)
index 0000000..a0d6044
--- /dev/null
@@ -0,0 +1,67 @@
+/* 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 */