diff options
author | Ray Strode <rstrode@redhat.com> | 2009-09-16 18:27:53 -0400 |
---|---|---|
committer | Ray Strode <rstrode@redhat.com> | 2009-09-28 17:55:19 -0400 |
commit | 920051bc55de0a70439afa70e8a7d030caf4b387 (patch) | |
tree | 256fa4351985d42b38b7c01aeb45cb804a4321d7 | |
parent | 069839a58b971bce8e3904b1c1237bf78e37318f (diff) |
[drm] Add start of a drm plugin
This commit adds most of the non-driver specific bits
necessary for backing a renderer plugin.
Subsequent commits will add the driver specific bits.
-rw-r--r-- | configure.ac | 5 | ||||
-rwxr-xr-x | scripts/plymouth-populate-initrd.in | 1 | ||||
-rw-r--r-- | src/libplybootsplash/ply-renderer.c | 1 | ||||
-rw-r--r-- | src/plugins/renderers/Makefile.am | 2 | ||||
-rw-r--r-- | src/plugins/renderers/drm/Makefile.am | 21 | ||||
-rw-r--r-- | src/plugins/renderers/drm/plugin.c | 1049 | ||||
-rw-r--r-- | src/plugins/renderers/drm/ply-renderer-driver.h | 67 |
7 files changed, 1145 insertions, 1 deletions
diff --git a/configure.ac b/configure.ac index df076e2c..0758dcad 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/scripts/plymouth-populate-initrd.in b/scripts/plymouth-populate-initrd.in index 42b119c3..14eb6fc7 100755 --- a/scripts/plymouth-populate-initrd.in +++ b/scripts/plymouth-populate-initrd.in @@ -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 diff --git a/src/libplybootsplash/ply-renderer.c b/src/libplybootsplash/ply-renderer.c index 9320030f..5410feb8 100644 --- a/src/libplybootsplash/ply-renderer.c +++ b/src/libplybootsplash/ply-renderer.c @@ -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 }; diff --git a/src/plugins/renderers/Makefile.am b/src/plugins/renderers/Makefile.am index 2a9e8a0c..2fadbf40 100644 --- a/src/plugins/renderers/Makefile.am +++ b/src/plugins/renderers/Makefile.am @@ -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 index 00000000..328b881a --- /dev/null +++ b/src/plugins/renderers/drm/Makefile.am @@ -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 index 00000000..67a6bea2 --- /dev/null +++ b/src/plugins/renderers/drm/plugin.c @@ -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 index 00000000..a0d6044a --- /dev/null +++ b/src/plugins/renderers/drm/ply-renderer-driver.h @@ -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 */ |