[mutter] Move cogl KMS winsys into the native backend



commit e420f386d2b97fc64a49cbe56de5e17430430ae4
Author: Jonas Ådahl <jadahl gmail com>
Date:   Mon May 9 19:51:29 2016 +0800

    Move cogl KMS winsys into the native backend
    
    Move the KMS interaction from cogl into mutter, where most of the other
    KMS interaction already takes place. This also removes dead code which
    were only excercised when non-mutter callers used the cogl KMS backend.
    
    The cogl KMS API was updated to pass via MetaRendererNative instead of
    via the different cogl objects.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=768976

 cogl/cogl/Makefile.am                             |   10 +-
 cogl/cogl/cogl-kms-display.h                      |  119 --
 cogl/cogl/cogl-kms-renderer.h                     |   74 --
 cogl/cogl/cogl-mutter.h                           |    1 -
 cogl/cogl/cogl-renderer-private.h                 |    4 -
 cogl/cogl/cogl-renderer.c                         |   10 -
 cogl/cogl/cogl-renderer.h                         |    3 +-
 cogl/cogl/cogl.h                                  |    4 -
 cogl/cogl/winsys/cogl-winsys-egl-kms-private.h    |   41 -
 cogl/cogl/winsys/cogl-winsys-egl-kms.c            | 1335 ---------------------
 src/backends/native/meta-cursor-renderer-native.c |   14 +-
 src/backends/native/meta-launcher.c               |   18 +-
 src/backends/native/meta-monitor-manager-kms.c    |   48 +-
 src/backends/native/meta-renderer-native.c        |  941 +++++++++++++++-
 src/backends/native/meta-renderer-native.h        |   29 +
 15 files changed, 1007 insertions(+), 1644 deletions(-)
---
diff --git a/cogl/cogl/Makefile.am b/cogl/cogl/Makefile.am
index fe0f3fb..902a1dd 100644
--- a/cogl/cogl/Makefile.am
+++ b/cogl/cogl/Makefile.am
@@ -430,14 +430,6 @@ endif
 if SUPPORT_WAYLAND_EGL_SERVER
 cogl_experimental_h += cogl-wayland-server.h
 endif
-if SUPPORT_EGL_PLATFORM_KMS
-cogl_experimental_h += \
-       cogl-kms-renderer.h \
-       cogl-kms-display.h
-cogl_sources_c += \
-       winsys/cogl-winsys-egl-kms.c \
-       winsys/cogl-winsys-egl-kms-private.h
-endif
 if SUPPORT_EGL_PLATFORM_XLIB
 cogl_sources_c += \
        winsys/cogl-winsys-egl-x11.c \
@@ -474,7 +466,7 @@ libmutter_cogl_la_LDFLAGS = \
        -avoid-version \
        -export-dynamic \
        -rpath $(mutterlibdir) \
-       -export-symbols-regex 
"^(cogl|_cogl_debug_flags|_cogl_atlas_new|_cogl_atlas_add_reorganize_callback|_cogl_atlas_reserve_space|_cogl_callback|_cogl_util_get_eye_planes_for_screen_poly|_cogl_atlas_texture_remove_reorganize_callback|_cogl_atlas_texture_add_reorganize_callback|_cogl_texture_get_format|_cogl_texture_foreach_sub_texture_in_region|_cogl_profile_trace_message|_cogl_context_get_default|_cogl_framebuffer_get_stencil_bits|_cogl_clip_stack_push_rectangle|_cogl_framebuffer_get_modelview_stack|_cogl_object_default_unref|_cogl_pipeline_foreach_layer_internal|_cogl_clip_stack_push_primitive|_cogl_buffer_unmap_for_fill_or_fallback|_cogl_framebuffer_draw_primitive|_cogl_debug_instances|_cogl_framebuffer_get_projection_stack|_cogl_pipeline_layer_get_texture|_cogl_buffer_map_for_fill_or_fallback|_cogl_texture_can_hardware_repeat|_cogl_pipeline_prune_to_n_layers|_cogl_primitive_draw|test_|unit_test_|_cogl_winsys_egl_kms_get_vtable|_cogl_winsys_glx_get_vtable|_cogl_winsys_egl_xl
 ib_get_vtable).*"
+       -export-symbols-regex 
"^(cogl|_cogl_debug_flags|_cogl_atlas_new|_cogl_atlas_add_reorganize_callback|_cogl_atlas_reserve_space|_cogl_callback|_cogl_util_get_eye_planes_for_screen_poly|_cogl_atlas_texture_remove_reorganize_callback|_cogl_atlas_texture_add_reorganize_callback|_cogl_texture_get_format|_cogl_texture_foreach_sub_texture_in_region|_cogl_profile_trace_message|_cogl_context_get_default|_cogl_framebuffer_get_stencil_bits|_cogl_clip_stack_push_rectangle|_cogl_framebuffer_get_modelview_stack|_cogl_object_default_unref|_cogl_pipeline_foreach_layer_internal|_cogl_clip_stack_push_primitive|_cogl_buffer_unmap_for_fill_or_fallback|_cogl_framebuffer_draw_primitive|_cogl_debug_instances|_cogl_framebuffer_get_projection_stack|_cogl_pipeline_layer_get_texture|_cogl_buffer_map_for_fill_or_fallback|_cogl_texture_can_hardware_repeat|_cogl_pipeline_prune_to_n_layers|_cogl_primitive_draw|test_|unit_test_|_cogl_winsys_glx_get_vtable|_cogl_winsys_egl_xlib_get_vtable|_cogl_winsys_egl_g
 
et_vtable|_cogl_closure_disconnect|_cogl_onscreen_notify_complete|_cogl_onscreen_notify_frame_sync|_cogl_winsys_egl_renderer_connect_common|_cogl_winsys_error_quark|_cogl_set_error|_cogl_poll_renderer_add_fd|_cogl_poll_renderer_add_idle|_cogl_framebuffer_winsys_update_size|_cogl_winsys_egl_make_current).*"
 
 libmutter_cogl_la_SOURCES = $(cogl_sources_c)
 nodist_libmutter_cogl_la_SOURCES = $(BUILT_SOURCES)
diff --git a/cogl/cogl/cogl-mutter.h b/cogl/cogl/cogl-mutter.h
index 672bede..d490a9c 100644
--- a/cogl/cogl/cogl-mutter.h
+++ b/cogl/cogl/cogl-mutter.h
@@ -38,7 +38,6 @@
 #include <cogl/cogl-meta-texture.h>
 #include <cogl/cogl-error-private.h>
 #include <cogl/cogl-renderer-private.h>
-#include <cogl/winsys/cogl-winsys-egl-kms-private.h>
 #include <cogl/winsys/cogl-winsys-egl-private.h>
 #include <cogl/winsys/cogl-winsys-private.h>
 
diff --git a/cogl/cogl/cogl-renderer-private.h b/cogl/cogl/cogl-renderer-private.h
index 81d956f..8627b6c 100644
--- a/cogl/cogl/cogl-renderer-private.h
+++ b/cogl/cogl/cogl-renderer-private.h
@@ -76,10 +76,6 @@ struct _CoglRenderer
     [COGL_FLAGS_N_LONGS_FOR_SIZE (COGL_N_PRIVATE_FEATURES)];
   GModule *libgl_module;
 
-#if defined (COGL_HAS_EGL_PLATFORM_KMS_SUPPORT)
-  int kms_fd;
-#endif
-
   /* List of callback functions that will be given every native event */
   GSList *event_filters;
   void *winsys;
diff --git a/cogl/cogl/cogl-renderer.c b/cogl/cogl/cogl-renderer.c
index 0272163..51a04ff 100644
--- a/cogl/cogl/cogl-renderer.c
+++ b/cogl/cogl/cogl-renderer.c
@@ -55,9 +55,6 @@
 #ifdef COGL_HAS_EGL_PLATFORM_XLIB_SUPPORT
 #include "cogl-winsys-egl-x11-private.h"
 #endif
-#ifdef COGL_HAS_EGL_PLATFORM_KMS_SUPPORT
-#include "cogl-winsys-egl-kms-private.h"
-#endif
 #ifdef COGL_HAS_GLX_SUPPORT
 #include "cogl-winsys-glx-private.h"
 #endif
@@ -167,9 +164,6 @@ static CoglWinsysVtableGetter _cogl_winsys_vtable_getters[] =
 #ifdef COGL_HAS_EGL_PLATFORM_XLIB_SUPPORT
   _cogl_winsys_egl_xlib_get_vtable,
 #endif
-#ifdef COGL_HAS_EGL_PLATFORM_KMS_SUPPORT
-  _cogl_winsys_egl_kms_get_vtable,
-#endif
   _cogl_winsys_stub_get_vtable,
 };
 
@@ -243,10 +237,6 @@ cogl_renderer_new (void)
   renderer->xlib_enable_event_retrieval = TRUE;
 #endif
 
-#ifdef COGL_HAS_EGL_PLATFORM_KMS_SUPPORT
-  renderer->kms_fd = -1;
-#endif
-
   return _cogl_renderer_object_new (renderer);
 }
 
diff --git a/cogl/cogl/cogl-renderer.h b/cogl/cogl/cogl-renderer.h
index 20c7906..88a8c08 100644
--- a/cogl/cogl/cogl-renderer.h
+++ b/cogl/cogl/cogl-renderer.h
@@ -161,7 +161,6 @@ cogl_renderer_new (void);
  * @COGL_WINSYS_ID_STUB: Use the no-op stub backend
  * @COGL_WINSYS_ID_GLX: Use the GLX window system binding API
  * @COGL_WINSYS_ID_EGL_XLIB: Use EGL with the X window system via XLib
- * @COGL_WINSYS_ID_EGL_KMS: Use EGL with the KMS platform
  *
  * Identifies specific window system backends that Cogl supports.
  *
@@ -174,7 +173,7 @@ typedef enum
   COGL_WINSYS_ID_STUB,
   COGL_WINSYS_ID_GLX,
   COGL_WINSYS_ID_EGL_XLIB,
-  COGL_WINSYS_ID_EGL_KMS,
+  COGL_WINSYS_ID_CUSTOM,
 } CoglWinsysID;
 
 /**
diff --git a/cogl/cogl/cogl.h b/cogl/cogl/cogl.h
index 778fdda..3d4ad0b 100644
--- a/cogl/cogl/cogl.h
+++ b/cogl/cogl/cogl.h
@@ -138,10 +138,6 @@
 #include <cogl/cogl-frame-info.h>
 #include <cogl/cogl-poll.h>
 #include <cogl/cogl-fence.h>
-#if defined (COGL_HAS_EGL_PLATFORM_KMS_SUPPORT)
-#include <cogl/cogl-kms-renderer.h>
-#include <cogl/cogl-kms-display.h>
-#endif
 #ifdef COGL_HAS_GLIB_SUPPORT
 #include <cogl/cogl-glib-source.h>
 #endif
diff --git a/src/backends/native/meta-cursor-renderer-native.c 
b/src/backends/native/meta-cursor-renderer-native.c
index c20c3ec..9219441 100644
--- a/src/backends/native/meta-cursor-renderer-native.c
+++ b/src/backends/native/meta-cursor-renderer-native.c
@@ -34,7 +34,9 @@
 #include <meta/util.h>
 #include <meta/meta-backend.h>
 
-#include "meta-monitor-manager-private.h"
+#include "backends/meta-backend-private.h"
+#include "backends/meta-monitor-manager-private.h"
+#include "backends/native/meta-renderer-native.h"
 #include "meta/boxes.h"
 
 #ifndef DRM_CAP_CURSOR_WIDTH
@@ -657,7 +659,6 @@ static void
 meta_cursor_renderer_native_init (MetaCursorRendererNative *native)
 {
   MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native);
-  CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
   MetaMonitorManager *monitors;
 
   monitors = meta_monitor_manager_get ();
@@ -667,9 +668,12 @@ meta_cursor_renderer_native_init (MetaCursorRendererNative *native)
 #if defined(CLUTTER_WINDOWING_EGL)
   if (clutter_check_windowing_backend (CLUTTER_WINDOWING_EGL))
     {
-      CoglRenderer *cogl_renderer = cogl_display_get_renderer (cogl_context_get_display (ctx));
-      priv->drm_fd = cogl_kms_renderer_get_kms_fd (cogl_renderer);
-      priv->gbm = cogl_kms_renderer_get_gbm (cogl_renderer);
+      MetaBackend *backend = meta_get_backend ();
+      MetaRenderer *renderer = meta_backend_get_renderer (backend);
+      MetaRendererNative *renderer_native = META_RENDERER_NATIVE (renderer);
+
+      priv->drm_fd = meta_renderer_native_get_kms_fd (renderer_native);
+      priv->gbm = meta_renderer_native_get_gbm (renderer_native);
 
       uint64_t width, height;
       if (drmGetCap (priv->drm_fd, DRM_CAP_CURSOR_WIDTH, &width) == 0 &&
diff --git a/src/backends/native/meta-launcher.c b/src/backends/native/meta-launcher.c
index 5991fc7..ea28e5a 100644
--- a/src/backends/native/meta-launcher.c
+++ b/src/backends/native/meta-launcher.c
@@ -45,6 +45,7 @@
 #include "backends/meta-backend-private.h"
 #include "meta-cursor-renderer-native.h"
 #include "meta-idle-monitor-native.h"
+#include "meta-renderer-native.h"
 
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(GUdevDevice, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC(GUdevClient, g_object_unref)
@@ -108,21 +109,20 @@ get_seat_proxy (GCancellable *cancellable,
 static void
 session_unpause (void)
 {
-  ClutterBackend *clutter_backend;
-  CoglContext *cogl_context;
-  CoglDisplay *cogl_display;
+  MetaBackend *backend;
+  MetaRenderer *renderer;
 
-  clutter_backend = clutter_get_default_backend ();
-  cogl_context = clutter_backend_get_cogl_context (clutter_backend);
-  cogl_display = cogl_context_get_display (cogl_context);
-  cogl_kms_display_queue_modes_reset (cogl_display);
+  backend = meta_get_backend ();
+  renderer = meta_backend_get_renderer (backend);
+  meta_renderer_native_queue_modes_reset (META_RENDERER_NATIVE (renderer));
 
   clutter_evdev_reclaim_devices ();
   clutter_egl_thaw_master_clock ();
 
   {
     MetaBackend *backend = meta_get_backend ();
-    MetaCursorRenderer *renderer = meta_backend_get_cursor_renderer (backend);
+    MetaCursorRendererNative *cursor_renderer_native =
+      META_CURSOR_RENDERER_NATIVE (meta_backend_get_cursor_renderer (backend));
     ClutterActor *stage = meta_backend_get_stage (backend);
 
     /* When we mode-switch back, we need to immediately queue a redraw
@@ -130,7 +130,7 @@ session_unpause (void)
      * update. */
 
     clutter_actor_queue_redraw (stage);
-    meta_cursor_renderer_native_force_update (META_CURSOR_RENDERER_NATIVE (renderer));
+    meta_cursor_renderer_native_force_update (cursor_renderer_native);
     meta_idle_monitor_native_reset_idletime (meta_idle_monitor_get_core ());
   }
 }
diff --git a/src/backends/native/meta-monitor-manager-kms.c b/src/backends/native/meta-monitor-manager-kms.c
index a28ef79..51aed36 100644
--- a/src/backends/native/meta-monitor-manager-kms.c
+++ b/src/backends/native/meta-monitor-manager-kms.c
@@ -25,6 +25,8 @@
 
 #include "meta-monitor-manager-kms.h"
 #include "meta-monitor-config.h"
+#include "meta-backend-private.h"
+#include "meta-renderer-native.h"
 
 #include <string.h>
 #include <stdlib.h>
@@ -952,9 +954,8 @@ meta_monitor_manager_kms_set_power_save_mode (MetaMonitorManager *manager,
                                               MetaPowerSave       mode)
 {
   MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager);
-  ClutterBackend *backend;
-  CoglContext *cogl_context;
-  CoglDisplay *cogl_display;
+  MetaBackend *backend;
+  MetaRenderer *renderer;
   uint64_t state;
   unsigned i;
 
@@ -995,13 +996,13 @@ meta_monitor_manager_kms_set_power_save_mode (MetaMonitorManager *manager,
         }
     }
 
-  backend = clutter_get_default_backend ();
-  cogl_context = clutter_backend_get_cogl_context (backend);
-  cogl_display = cogl_context_get_display (cogl_context);
+  backend = meta_get_backend ();
+  renderer = meta_backend_get_renderer (backend);
 
   for (i = 0; i < manager->n_crtcs; i++)
-    cogl_kms_display_set_ignore_crtc (cogl_display, manager->crtcs[i].crtc_id,
-                                      mode != META_POWER_SAVE_ON);
+    meta_renderer_native_set_ignore_crtc (META_RENDERER_NATIVE (renderer),
+                                          manager->crtcs[i].crtc_id,
+                                          mode != META_POWER_SAVE_ON);
 }
 
 static void
@@ -1061,9 +1062,8 @@ meta_monitor_manager_kms_apply_configuration (MetaMonitorManager *manager,
                                               unsigned int         n_outputs)
 {
   MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager);
-  ClutterBackend *backend;
-  CoglContext *cogl_context;
-  CoglDisplay *cogl_display;
+  MetaBackend *backend;
+  MetaRenderer *renderer;
   unsigned i;
   GPtrArray *cogl_crtcs;
   int screen_width, screen_height;
@@ -1191,13 +1191,15 @@ meta_monitor_manager_kms_apply_configuration (MetaMonitorManager *manager,
       crtc->current_mode = NULL;
     }
 
-  backend = clutter_get_default_backend ();
-  cogl_context = clutter_backend_get_cogl_context (backend);
-  cogl_display = cogl_context_get_display (cogl_context);
+  backend = meta_get_backend ();
+  renderer = meta_backend_get_renderer (backend);
 
   error = NULL;
-  ok = cogl_kms_display_set_layout (cogl_display, screen_width, screen_height,
-                                    (CoglKmsCrtc**)cogl_crtcs->pdata, cogl_crtcs->len, &error);
+  ok = meta_renderer_native_set_layout (META_RENDERER_NATIVE (renderer),
+                                        screen_width, screen_height,
+                                        (CoglKmsCrtc**)cogl_crtcs->pdata,
+                                        cogl_crtcs->len,
+                                        &error);
   g_ptr_array_unref (cogl_crtcs);
 
   if (!ok)
@@ -1296,17 +1298,11 @@ on_uevent (GUdevClient *client,
 static void
 meta_monitor_manager_kms_init (MetaMonitorManagerKms *manager_kms)
 {
-  ClutterBackend *backend;
-  CoglContext *cogl_context;
-  CoglDisplay *cogl_display;
-  CoglRenderer *cogl_renderer;
+  MetaBackend *backend = meta_get_backend ();
+  MetaRenderer *renderer = meta_backend_get_renderer (backend);
+  MetaRendererNative *renderer_native = META_RENDERER_NATIVE (renderer);
 
-  backend = clutter_get_default_backend ();
-  cogl_context = clutter_backend_get_cogl_context (backend);
-  cogl_display = cogl_context_get_display (cogl_context);
-  cogl_renderer = cogl_display_get_renderer (cogl_display);
-
-  manager_kms->fd = cogl_kms_renderer_get_kms_fd (cogl_renderer);
+  manager_kms->fd = meta_renderer_native_get_kms_fd (renderer_native);
 
   drmSetClientCap (manager_kms->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
 
diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c
index 16e2aa4..f5a1f48 100644
--- a/src/backends/native/meta-renderer-native.c
+++ b/src/backends/native/meta-renderer-native.c
@@ -1,6 +1,7 @@
 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
 
 /*
+ * Copyright (C) 2011 Intel Corporation.
  * Copyright (C) 2016 Red Hat
  *
  * Permission is hereby granted, free of charge, to any person
@@ -23,14 +24,28 @@
  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  *
- * Written by:
- *     Jonas Ådahl <jadahl gmail com>
+ * Authors:
+ *   Rob Bradford <rob linux intel com> (from cogl-winsys-egl-kms.c)
+ *   Kristian Høgsberg (from eglkms.c)
+ *   Benjamin Franzke (from eglkms.c)
+ *   Robert Bragg <robert linux intel com> (from cogl-winsys-egl-kms.c)
+ *   Neil Roberts <neil linux intel com> (from cogl-winsys-egl-kms.c)
+ *   Jonas Ådahl <jadahl redhat com>
+ *
  */
 
 #include "config.h"
 
+#include <errno.h>
+#include <fcntl.h>
 #include <glib-object.h>
+#include <gbm.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <xf86drm.h>
 
+#include "backends/meta-backend-private.h"
 #include "backends/native/meta-renderer-native.h"
 #include "cogl/cogl.h"
 
@@ -52,16 +67,932 @@ struct _MetaRendererNative
 
 G_DEFINE_TYPE (MetaRendererNative, meta_renderer_native, META_TYPE_RENDERER)
 
+static const CoglWinsysEGLVtable _cogl_winsys_egl_vtable;
+static const CoglWinsysVtable *parent_vtable;
+
+typedef struct _CoglRendererKMS
+{
+  int fd;
+  struct gbm_device *gbm;
+  CoglClosure *swap_notify_idle;
+  CoglBool     page_flips_not_supported;
+} CoglRendererKMS;
+
+typedef struct _CoglDisplayKMS
+{
+  GList *crtcs;
+
+  int width, height;
+  CoglBool pending_set_crtc;
+  struct gbm_surface *dummy_gbm_surface;
+
+  CoglOnscreen *onscreen;
+} CoglDisplayKMS;
+
+typedef struct _CoglFlipKMS
+{
+  CoglOnscreen *onscreen;
+  int pending;
+} CoglFlipKMS;
+
+typedef struct _CoglOnscreenKMS
+{
+  struct gbm_surface *surface;
+  uint32_t current_fb_id;
+  uint32_t next_fb_id;
+  struct gbm_bo *current_bo;
+  struct gbm_bo *next_bo;
+  CoglBool pending_swap_notify;
+
+  EGLSurface *pending_egl_surface;
+  struct gbm_surface *pending_surface;
+} CoglOnscreenKMS;
+
+static void
+_cogl_winsys_renderer_disconnect (CoglRenderer *renderer)
+{
+  CoglRendererEGL *egl_renderer = renderer->winsys;
+  CoglRendererKMS *kms_renderer = egl_renderer->platform;
+
+  if (egl_renderer->edpy != EGL_NO_DISPLAY)
+    eglTerminate (egl_renderer->edpy);
+
+  if (kms_renderer->gbm != NULL)
+    gbm_device_destroy (kms_renderer->gbm);
+
+  g_slice_free (CoglRendererKMS, kms_renderer);
+  g_slice_free (CoglRendererEGL, egl_renderer);
+}
+
+static void
+flush_pending_swap_notify_cb (void *data,
+                              void *user_data)
+{
+  CoglFramebuffer *framebuffer = data;
+
+  if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN)
+    {
+      CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
+      CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
+      CoglOnscreenKMS *kms_onscreen = egl_onscreen->platform;
+
+      if (kms_onscreen->pending_swap_notify)
+        {
+          CoglFrameInfo *info = g_queue_pop_head (&onscreen->pending_frame_infos);
+
+          _cogl_onscreen_notify_frame_sync (onscreen, info);
+          _cogl_onscreen_notify_complete (onscreen, info);
+          kms_onscreen->pending_swap_notify = FALSE;
+
+          cogl_object_unref (info);
+        }
+    }
+}
+
+static void
+flush_pending_swap_notify_idle (void *user_data)
+{
+  CoglContext *context = user_data;
+  CoglRendererEGL *egl_renderer = context->display->renderer->winsys;
+  CoglRendererKMS *kms_renderer = egl_renderer->platform;
+
+  /* This needs to be disconnected before invoking the callbacks in
+   * case the callbacks cause it to be queued again */
+  _cogl_closure_disconnect (kms_renderer->swap_notify_idle);
+  kms_renderer->swap_notify_idle = NULL;
+
+  g_list_foreach (context->framebuffers,
+                  flush_pending_swap_notify_cb,
+                  NULL);
+}
+
+static void
+free_current_bo (CoglOnscreen *onscreen)
+{
+  CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
+  CoglOnscreenKMS *kms_onscreen = egl_onscreen->platform;
+  CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context;
+  CoglRenderer *renderer = context->display->renderer;
+  CoglRendererEGL *egl_renderer = renderer->winsys;
+  CoglRendererKMS *kms_renderer = egl_renderer->platform;
+
+  if (kms_onscreen->current_fb_id)
+    {
+      drmModeRmFB (kms_renderer->fd,
+                   kms_onscreen->current_fb_id);
+      kms_onscreen->current_fb_id = 0;
+    }
+  if (kms_onscreen->current_bo)
+    {
+      gbm_surface_release_buffer (kms_onscreen->surface,
+                                  kms_onscreen->current_bo);
+      kms_onscreen->current_bo = NULL;
+    }
+}
+
+static void
+queue_swap_notify_for_onscreen (CoglOnscreen *onscreen)
+{
+  CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
+  CoglOnscreenKMS *kms_onscreen = egl_onscreen->platform;
+  CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context;
+  CoglRenderer *renderer = context->display->renderer;
+  CoglRendererEGL *egl_renderer = renderer->winsys;
+  CoglRendererKMS *kms_renderer = egl_renderer->platform;
+
+  /* We only want to notify that the swap is complete when the
+   * application calls cogl_context_dispatch so instead of
+   * immediately notifying we queue an idle callback */
+  if (!kms_renderer->swap_notify_idle)
+    {
+      kms_renderer->swap_notify_idle =
+        _cogl_poll_renderer_add_idle (renderer,
+                                      flush_pending_swap_notify_idle,
+                                      context,
+                                      NULL);
+    }
+
+  kms_onscreen->pending_swap_notify = TRUE;
+}
+
+static void
+process_flip (CoglFlipKMS *flip)
+{
+  /* We're only ready to dispatch a swap notification once all outputs
+   * have flipped... */
+  flip->pending--;
+  if (flip->pending == 0)
+    {
+      CoglOnscreen *onscreen = flip->onscreen;
+      CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
+      CoglOnscreenKMS *kms_onscreen = egl_onscreen->platform;
+
+      queue_swap_notify_for_onscreen (onscreen);
+
+      free_current_bo (onscreen);
+
+      kms_onscreen->current_fb_id = kms_onscreen->next_fb_id;
+      kms_onscreen->next_fb_id = 0;
+
+      kms_onscreen->current_bo = kms_onscreen->next_bo;
+      kms_onscreen->next_bo = NULL;
+
+      cogl_object_unref (flip->onscreen);
+
+      g_slice_free (CoglFlipKMS, flip);
+    }
+}
+
+static void
+page_flip_handler (int fd,
+                   unsigned int frame,
+                   unsigned int sec,
+                   unsigned int usec,
+                   void *data)
+{
+  CoglFlipKMS *flip = data;
+
+  process_flip (flip);
+}
+
+static void
+handle_drm_event (CoglRendererKMS *kms_renderer)
+{
+  drmEventContext evctx;
+
+  if (kms_renderer->page_flips_not_supported)
+    return;
+
+  memset (&evctx, 0, sizeof evctx);
+  evctx.version = DRM_EVENT_CONTEXT_VERSION;
+  evctx.page_flip_handler = page_flip_handler;
+  drmHandleEvent (kms_renderer->fd, &evctx);
+}
+
+static void
+dispatch_kms_events (void *user_data, int revents)
+{
+  CoglRenderer *renderer = user_data;
+  CoglRendererEGL *egl_renderer = renderer->winsys;
+  CoglRendererKMS *kms_renderer = egl_renderer->platform;
+
+  if (!revents)
+    return;
+
+  handle_drm_event (kms_renderer);
+}
+
+static CoglBool
+_cogl_winsys_renderer_connect (CoglRenderer *cogl_renderer,
+                               CoglError **error)
+{
+  MetaBackend *backend = meta_get_backend ();
+  MetaRenderer *renderer = meta_backend_get_renderer (backend);
+  MetaRendererNative *renderer_native = META_RENDERER_NATIVE (renderer);
+  CoglRendererEGL *egl_renderer;
+  CoglRendererKMS *kms_renderer;
+
+  cogl_renderer->winsys = g_slice_new0 (CoglRendererEGL);
+  egl_renderer = cogl_renderer->winsys;
+
+  egl_renderer->platform_vtable = &_cogl_winsys_egl_vtable;
+  egl_renderer->platform = g_slice_new0 (CoglRendererKMS);
+  kms_renderer = egl_renderer->platform;
+
+  kms_renderer->fd = meta_renderer_native_get_kms_fd (renderer_native);
+
+  egl_renderer->edpy = EGL_NO_DISPLAY;
+
+  kms_renderer->gbm = gbm_create_device (kms_renderer->fd);
+  if (kms_renderer->gbm == NULL)
+    {
+      _cogl_set_error (error, COGL_WINSYS_ERROR,
+                       COGL_WINSYS_ERROR_INIT,
+                       "Couldn't create gbm device");
+      goto fail;
+    }
+
+  egl_renderer->edpy = eglGetDisplay ((EGLNativeDisplayType)kms_renderer->gbm);
+  if (egl_renderer->edpy == EGL_NO_DISPLAY)
+    {
+      _cogl_set_error (error, COGL_WINSYS_ERROR,
+                       COGL_WINSYS_ERROR_INIT,
+                       "Couldn't get eglDisplay");
+      goto fail;
+    }
+
+  if (!_cogl_winsys_egl_renderer_connect_common (cogl_renderer, error))
+    goto fail;
+
+  _cogl_poll_renderer_add_fd (cogl_renderer,
+                              kms_renderer->fd,
+                              COGL_POLL_FD_EVENT_IN,
+                              NULL, /* no prepare callback */
+                              dispatch_kms_events,
+                              cogl_renderer);
+
+  return TRUE;
+
+fail:
+  _cogl_winsys_renderer_disconnect (cogl_renderer);
+
+  return FALSE;
+}
+
+static void
+setup_crtc_modes (CoglDisplay *display, int fb_id)
+{
+  CoglDisplayEGL *egl_display = display->winsys;
+  CoglDisplayKMS *kms_display = egl_display->platform;
+  CoglRendererEGL *egl_renderer = display->renderer->winsys;
+  CoglRendererKMS *kms_renderer = egl_renderer->platform;
+  GList *l;
+
+  for (l = kms_display->crtcs; l; l = l->next)
+    {
+      CoglKmsCrtc *crtc = l->data;
+
+      int ret = drmModeSetCrtc (kms_renderer->fd,
+                                crtc->id,
+                                fb_id, crtc->x, crtc->y,
+                                crtc->connectors, crtc->count,
+                                crtc->count ? &crtc->mode : NULL);
+      if (ret)
+        g_warning ("Failed to set crtc mode %s: %m", crtc->mode.name);
+    }
+}
+
+static void
+flip_all_crtcs (CoglDisplay *display, CoglFlipKMS *flip, int fb_id)
+{
+  CoglDisplayEGL *egl_display = display->winsys;
+  CoglDisplayKMS *kms_display = egl_display->platform;
+  CoglRendererEGL *egl_renderer = display->renderer->winsys;
+  CoglRendererKMS *kms_renderer = egl_renderer->platform;
+  GList *l;
+  gboolean needs_flip = FALSE;
+
+  for (l = kms_display->crtcs; l; l = l->next)
+    {
+      CoglKmsCrtc *crtc = l->data;
+      int ret = 0;
+
+      if (crtc->count == 0 || crtc->ignore)
+        continue;
+
+      needs_flip = TRUE;
+
+      if (!kms_renderer->page_flips_not_supported)
+        {
+          ret = drmModePageFlip (kms_renderer->fd,
+                                 crtc->id, fb_id,
+                                 DRM_MODE_PAGE_FLIP_EVENT, flip);
+          if (ret != 0 && ret != -EACCES)
+            {
+              g_warning ("Failed to flip: %m");
+              kms_renderer->page_flips_not_supported = TRUE;
+              break;
+            }
+        }
+
+      if (ret == 0)
+        flip->pending++;
+    }
+
+  if (kms_renderer->page_flips_not_supported && needs_flip)
+    flip->pending = 1;
+}
+
+static void
+crtc_free (CoglKmsCrtc *crtc)
+{
+  g_free (crtc->connectors);
+  g_slice_free (CoglKmsCrtc, crtc);
+}
+
+static CoglKmsCrtc *
+crtc_copy (CoglKmsCrtc *from)
+{
+  CoglKmsCrtc *new;
+
+  new = g_slice_new (CoglKmsCrtc);
+
+  *new = *from;
+  new->connectors = g_memdup (from->connectors, from->count * sizeof(uint32_t));
+
+  return new;
+}
+
+static CoglBool
+_cogl_winsys_egl_display_setup (CoglDisplay *display,
+                                CoglError **error)
+{
+  CoglDisplayEGL *egl_display = display->winsys;
+  CoglDisplayKMS *kms_display;
+  CoglRendererEGL *egl_renderer = display->renderer->winsys;
+  CoglRendererKMS *kms_renderer = egl_renderer->platform;
+
+  kms_display = g_slice_new0 (CoglDisplayKMS);
+  egl_display->platform = kms_display;
+
+  /* Force a full modeset / drmModeSetCrtc on
+   * the first swap buffers call.
+   */
+  kms_display->pending_set_crtc = TRUE;
+
+  return TRUE;
+}
+
+static void
+_cogl_winsys_egl_display_destroy (CoglDisplay *display)
+{
+  CoglDisplayEGL *egl_display = display->winsys;
+  CoglDisplayKMS *kms_display = egl_display->platform;
+  CoglRenderer *renderer = display->renderer;
+  CoglRendererEGL *egl_renderer = renderer->winsys;
+  CoglRendererKMS *kms_renderer = egl_renderer->platform;
+  GList *l;
+
+  g_list_free_full (kms_display->crtcs, (GDestroyNotify) crtc_free);
+
+  g_slice_free (CoglDisplayKMS, egl_display->platform);
+}
+
+static CoglBool
+_cogl_winsys_egl_context_created (CoglDisplay *display,
+                                  CoglError **error)
+{
+  CoglDisplayEGL *egl_display = display->winsys;
+  CoglDisplayKMS *kms_display = egl_display->platform;
+  CoglRenderer *renderer = display->renderer;
+  CoglRendererEGL *egl_renderer = renderer->winsys;
+  CoglRendererKMS *kms_renderer = egl_renderer->platform;
+
+  if ((egl_renderer->private_features &
+       COGL_EGL_WINSYS_FEATURE_SURFACELESS_CONTEXT) == 0)
+    {
+      kms_display->dummy_gbm_surface =
+        gbm_surface_create (kms_renderer->gbm,
+                            16, 16,
+                            GBM_FORMAT_XRGB8888,
+                            GBM_BO_USE_RENDERING);
+      if (!kms_display->dummy_gbm_surface)
+        {
+          _cogl_set_error (error, COGL_WINSYS_ERROR,
+                           COGL_WINSYS_ERROR_CREATE_CONTEXT,
+                           "Failed to create dummy GBM surface");
+          return FALSE;
+        }
+
+      egl_display->dummy_surface =
+        eglCreateWindowSurface (egl_renderer->edpy,
+                                egl_display->egl_config,
+                                (EGLNativeWindowType)
+                                kms_display->dummy_gbm_surface,
+                                NULL);
+      if (egl_display->dummy_surface == EGL_NO_SURFACE)
+        {
+          _cogl_set_error (error, COGL_WINSYS_ERROR,
+                           COGL_WINSYS_ERROR_CREATE_CONTEXT,
+                           "Failed to create dummy EGL surface");
+          return FALSE;
+        }
+    }
+
+  if (!_cogl_winsys_egl_make_current (display,
+                                      egl_display->dummy_surface,
+                                      egl_display->dummy_surface,
+                                      egl_display->egl_context))
+    {
+      _cogl_set_error (error, COGL_WINSYS_ERROR,
+                   COGL_WINSYS_ERROR_CREATE_CONTEXT,
+                   "Failed to make context current");
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static void
+_cogl_winsys_egl_cleanup_context (CoglDisplay *display)
+{
+  CoglDisplayEGL *egl_display = display->winsys;
+  CoglDisplayKMS *kms_display = egl_display->platform;
+  CoglRenderer *renderer = display->renderer;
+  CoglRendererEGL *egl_renderer = renderer->winsys;
+
+  if (egl_display->dummy_surface != EGL_NO_SURFACE)
+    {
+      eglDestroySurface (egl_renderer->edpy, egl_display->dummy_surface);
+      egl_display->dummy_surface = EGL_NO_SURFACE;
+    }
+
+  if (kms_display->dummy_gbm_surface != NULL)
+    {
+      gbm_surface_destroy (kms_display->dummy_gbm_surface);
+      kms_display->dummy_gbm_surface = NULL;
+    }
+}
+
+static void
+_cogl_winsys_onscreen_swap_buffers_with_damage (CoglOnscreen *onscreen,
+                                                const int *rectangles,
+                                                int n_rectangles)
+{
+  CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context;
+  CoglDisplayEGL *egl_display = context->display->winsys;
+  CoglDisplayKMS *kms_display = egl_display->platform;
+  CoglRenderer *renderer = context->display->renderer;
+  CoglRendererEGL *egl_renderer = renderer->winsys;
+  CoglRendererKMS *kms_renderer = egl_renderer->platform;
+  CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
+  CoglOnscreenKMS *kms_onscreen = egl_onscreen->platform;
+  uint32_t handle, stride;
+  CoglFlipKMS *flip;
+
+  /* If we already have a pending swap then block until it completes */
+  while (kms_onscreen->next_fb_id != 0)
+    handle_drm_event (kms_renderer);
+
+  if (kms_onscreen->pending_egl_surface)
+    {
+      eglDestroySurface (egl_renderer->edpy, egl_onscreen->egl_surface);
+      egl_onscreen->egl_surface = kms_onscreen->pending_egl_surface;
+      kms_onscreen->pending_egl_surface = NULL;
+
+      _cogl_framebuffer_winsys_update_size (COGL_FRAMEBUFFER (kms_display->onscreen),
+                                            kms_display->width, kms_display->height);
+      context->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_BIND;
+    }
+  parent_vtable->onscreen_swap_buffers_with_damage (onscreen,
+                                                    rectangles,
+                                                    n_rectangles);
+
+  if (kms_onscreen->pending_surface)
+    {
+      free_current_bo (onscreen);
+      if (kms_onscreen->surface)
+        gbm_surface_destroy (kms_onscreen->surface);
+      kms_onscreen->surface = kms_onscreen->pending_surface;
+      kms_onscreen->pending_surface = NULL;
+    }
+  /* Now we need to set the CRTC to whatever is the front buffer */
+  kms_onscreen->next_bo = gbm_surface_lock_front_buffer (kms_onscreen->surface);
+
+  stride = gbm_bo_get_stride (kms_onscreen->next_bo);
+  handle = gbm_bo_get_handle (kms_onscreen->next_bo).u32;
+
+  if (drmModeAddFB (kms_renderer->fd,
+                    kms_display->width,
+                    kms_display->height,
+                    24, /* depth */
+                    32, /* bpp */
+                    stride,
+                    handle,
+                    &kms_onscreen->next_fb_id))
+    {
+      g_warning ("Failed to create new back buffer handle: %m");
+      gbm_surface_release_buffer (kms_onscreen->surface,
+                                  kms_onscreen->next_bo);
+      kms_onscreen->next_bo = NULL;
+      kms_onscreen->next_fb_id = 0;
+      return;
+    }
+
+  /* If this is the first framebuffer to be presented then we now setup the
+   * crtc modes, else we flip from the previous buffer */
+  if (kms_display->pending_set_crtc)
+    {
+      setup_crtc_modes (context->display, kms_onscreen->next_fb_id);
+      kms_display->pending_set_crtc = FALSE;
+    }
+
+  flip = g_slice_new0 (CoglFlipKMS);
+  flip->onscreen = onscreen;
+
+  flip_all_crtcs (context->display, flip, kms_onscreen->next_fb_id);
+
+  if (flip->pending == 0)
+    {
+      drmModeRmFB (kms_renderer->fd, kms_onscreen->next_fb_id);
+      gbm_surface_release_buffer (kms_onscreen->surface,
+                                  kms_onscreen->next_bo);
+      kms_onscreen->next_bo = NULL;
+      kms_onscreen->next_fb_id = 0;
+      g_slice_free (CoglFlipKMS, flip);
+      flip = NULL;
+
+      queue_swap_notify_for_onscreen (onscreen);
+    }
+  else
+    {
+      /* Ensure the onscreen remains valid while it has any pending flips... */
+      cogl_object_ref (flip->onscreen);
+
+      /* Process flip right away if we can't wait for vblank */
+      if (kms_renderer->page_flips_not_supported)
+        {
+          setup_crtc_modes (context->display, kms_onscreen->next_fb_id);
+          process_flip (flip);
+        }
+    }
+}
+
+static CoglBool
+_cogl_winsys_egl_context_init (CoglContext *context,
+                               CoglError **error)
+{
+  COGL_FLAGS_SET (context->features,
+                  COGL_FEATURE_ID_SWAP_BUFFERS_EVENT, TRUE);
+  /* TODO: remove this deprecated feature */
+  COGL_FLAGS_SET (context->winsys_features,
+                  COGL_WINSYS_FEATURE_SWAP_BUFFERS_EVENT,
+                  TRUE);
+  COGL_FLAGS_SET (context->winsys_features,
+                  COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT,
+                  TRUE);
+
+  return TRUE;
+}
+
+static CoglBool
+_cogl_winsys_onscreen_init (CoglOnscreen *onscreen,
+                            CoglError **error)
+{
+  CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+  CoglContext *context = framebuffer->context;
+  CoglDisplay *display = context->display;
+  CoglDisplayEGL *egl_display = display->winsys;
+  CoglDisplayKMS *kms_display = egl_display->platform;
+  CoglRenderer *renderer = display->renderer;
+  CoglRendererEGL *egl_renderer = renderer->winsys;
+  CoglRendererKMS *kms_renderer = egl_renderer->platform;
+  CoglOnscreenEGL *egl_onscreen;
+  CoglOnscreenKMS *kms_onscreen;
+
+  _COGL_RETURN_VAL_IF_FAIL (egl_display->egl_context, FALSE);
+
+  if (kms_display->onscreen)
+    {
+      _cogl_set_error (error, COGL_WINSYS_ERROR,
+                       COGL_WINSYS_ERROR_CREATE_ONSCREEN,
+                       "Cannot have multiple onscreens in the KMS platform");
+      return FALSE;
+    }
+
+  kms_display->onscreen = onscreen;
+
+  onscreen->winsys = g_slice_new0 (CoglOnscreenEGL);
+  egl_onscreen = onscreen->winsys;
+
+  kms_onscreen = g_slice_new0 (CoglOnscreenKMS);
+  egl_onscreen->platform = kms_onscreen;
+
+  /* If a kms_fd is set then the display width and height
+   * won't be available until meta_renderer_native_set_layout
+   * is called. In that case, defer creating the surface
+   * until then.
+   */
+  if (kms_display->width == 0 ||
+      kms_display->height == 0)
+    return TRUE;
+
+  kms_onscreen->surface =
+    gbm_surface_create (kms_renderer->gbm,
+                        kms_display->width,
+                        kms_display->height,
+                        GBM_FORMAT_XRGB8888,
+                        GBM_BO_USE_SCANOUT |
+                        GBM_BO_USE_RENDERING);
+
+  if (!kms_onscreen->surface)
+    {
+      _cogl_set_error (error, COGL_WINSYS_ERROR,
+                   COGL_WINSYS_ERROR_CREATE_ONSCREEN,
+                   "Failed to allocate surface");
+      return FALSE;
+    }
+
+  egl_onscreen->egl_surface =
+    eglCreateWindowSurface (egl_renderer->edpy,
+                            egl_display->egl_config,
+                            (EGLNativeWindowType) kms_onscreen->surface,
+                            NULL);
+  if (egl_onscreen->egl_surface == EGL_NO_SURFACE)
+    {
+      _cogl_set_error (error, COGL_WINSYS_ERROR,
+                   COGL_WINSYS_ERROR_CREATE_ONSCREEN,
+                   "Failed to allocate surface");
+      return FALSE;
+    }
+
+  _cogl_framebuffer_winsys_update_size (framebuffer,
+                                        kms_display->width,
+                                        kms_display->height);
+
+  return TRUE;
+}
+
+static void
+_cogl_winsys_onscreen_deinit (CoglOnscreen *onscreen)
+{
+  CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+  CoglContext *context = framebuffer->context;
+  CoglDisplay *display = context->display;
+  CoglDisplayEGL *egl_display = display->winsys;
+  CoglDisplayKMS *kms_display = egl_display->platform;
+  CoglRenderer *renderer = context->display->renderer;
+  CoglRendererEGL *egl_renderer = renderer->winsys;
+  CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
+  CoglOnscreenKMS *kms_onscreen;
+
+  /* If we never successfully allocated then there's nothing to do */
+  if (egl_onscreen == NULL)
+    return;
+
+  kms_display->onscreen = NULL;
+
+  kms_onscreen = egl_onscreen->platform;
+
+  /* flip state takes a reference on the onscreen so there should
+   * never be outstanding flips when we reach here. */
+  g_return_if_fail (kms_onscreen->next_fb_id == 0);
+
+  free_current_bo (onscreen);
+
+  if (egl_onscreen->egl_surface != EGL_NO_SURFACE)
+    {
+      eglDestroySurface (egl_renderer->edpy, egl_onscreen->egl_surface);
+      egl_onscreen->egl_surface = EGL_NO_SURFACE;
+    }
+
+  if (kms_onscreen->surface)
+    {
+      gbm_surface_destroy (kms_onscreen->surface);
+      kms_onscreen->surface = NULL;
+    }
+
+  g_slice_free (CoglOnscreenKMS, kms_onscreen);
+  g_slice_free (CoglOnscreenEGL, onscreen->winsys);
+  onscreen->winsys = NULL;
+}
+
+static const CoglWinsysEGLVtable
+_cogl_winsys_egl_vtable = {
+  .display_setup = _cogl_winsys_egl_display_setup,
+  .display_destroy = _cogl_winsys_egl_display_destroy,
+  .context_created = _cogl_winsys_egl_context_created,
+  .cleanup_context = _cogl_winsys_egl_cleanup_context,
+  .context_init = _cogl_winsys_egl_context_init
+};
+
+struct gbm_device *
+meta_renderer_native_get_gbm (MetaRendererNative *renderer_native)
+{
+  ClutterBackend *clutter_backend = clutter_get_default_backend ();
+  CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend);
+  CoglDisplay *cogl_display = cogl_context_get_display (cogl_context);
+  CoglRenderer *renderer = cogl_display->renderer;
+
+  if (renderer->connected)
+    {
+      CoglRendererEGL *egl_renderer = renderer->winsys;
+      CoglRendererKMS *kms_renderer = egl_renderer->platform;
+      return kms_renderer->gbm;
+    }
+  else
+    {
+      return NULL;
+    }
+}
+
+int
+meta_renderer_native_get_kms_fd (MetaRendererNative *renderer_native)
+{
+  return renderer_native->kms_fd;
+}
+
+void
+meta_renderer_native_queue_modes_reset (MetaRendererNative *renderer_native)
+{
+  ClutterBackend *clutter_backend = clutter_get_default_backend ();
+  CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend);
+  CoglDisplay *cogl_display = cogl_context_get_display (cogl_context);
+
+  if (cogl_display->setup)
+    {
+      CoglDisplayEGL *egl_display = cogl_display->winsys;
+      CoglDisplayKMS *kms_display = egl_display->platform;
+      kms_display->pending_set_crtc = TRUE;
+    }
+}
+
+gboolean
+meta_renderer_native_set_layout (MetaRendererNative *renderer_native,
+                                 int                 width,
+                                 int                 height,
+                                 CoglKmsCrtc       **crtcs,
+                                 int                 n_crtcs,
+                                 GError            **error)
+{
+  ClutterBackend *clutter_backend = clutter_get_default_backend ();
+  CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend);
+  CoglDisplay *cogl_display = cogl_context_get_display (cogl_context);
+  CoglDisplayEGL *egl_display = cogl_display->winsys;
+  CoglDisplayKMS *kms_display = egl_display->platform;
+  CoglRenderer *renderer = cogl_display->renderer;
+  CoglRendererEGL *egl_renderer = renderer->winsys;
+  CoglRendererKMS *kms_renderer = egl_renderer->platform;
+  GList *crtc_list;
+  int i;
+
+  if ((width != kms_display->width ||
+       height != kms_display->height) &&
+      kms_display->onscreen)
+    {
+      CoglOnscreenEGL *egl_onscreen = kms_display->onscreen->winsys;
+      CoglOnscreenKMS *kms_onscreen = egl_onscreen->platform;
+      struct gbm_surface *new_surface;
+      EGLSurface new_egl_surface;
+
+      /* Need to drop the GBM surface and create a new one */
+
+      new_surface = gbm_surface_create (kms_renderer->gbm,
+                                        width, height,
+                                        GBM_FORMAT_XRGB8888,
+                                        GBM_BO_USE_SCANOUT |
+                                        GBM_BO_USE_RENDERING);
+
+      if (!new_surface)
+        {
+          _cogl_set_error (error, COGL_WINSYS_ERROR,
+                           COGL_WINSYS_ERROR_CREATE_ONSCREEN,
+                           "Failed to allocate new surface");
+          return FALSE;
+        }
+
+      new_egl_surface =
+        eglCreateWindowSurface (egl_renderer->edpy,
+                                egl_display->egl_config,
+                                (EGLNativeWindowType) new_surface,
+                                NULL);
+      if (new_egl_surface == EGL_NO_SURFACE)
+        {
+          _cogl_set_error (error, COGL_WINSYS_ERROR,
+                           COGL_WINSYS_ERROR_CREATE_ONSCREEN,
+                           "Failed to allocate new surface");
+          gbm_surface_destroy (new_surface);
+          return FALSE;
+        }
+
+      if (kms_onscreen->pending_egl_surface)
+        eglDestroySurface (egl_renderer->edpy, kms_onscreen->pending_egl_surface);
+      if (kms_onscreen->pending_surface)
+        gbm_surface_destroy (kms_onscreen->pending_surface);
+
+      /* If there's already a surface, wait until the next swap to switch
+       * it out, otherwise, if we're just starting up we can use the new
+       * surface right away.
+       */
+      if (kms_onscreen->surface != NULL)
+        {
+          kms_onscreen->pending_surface = new_surface;
+          kms_onscreen->pending_egl_surface = new_egl_surface;
+        }
+      else
+        {
+          CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (kms_display->onscreen);
+
+          kms_onscreen->surface = new_surface;
+          egl_onscreen->egl_surface = new_egl_surface;
+
+          _cogl_framebuffer_winsys_update_size (framebuffer, width, height);
+        }
+    }
+
+  kms_display->width = width;
+  kms_display->height = height;
+
+  g_list_free_full (kms_display->crtcs, (GDestroyNotify) crtc_free);
+
+  crtc_list = NULL;
+  for (i = 0; i < n_crtcs; i++)
+    {
+      crtc_list = g_list_prepend (crtc_list, crtc_copy (crtcs[i]));
+    }
+  crtc_list = g_list_reverse (crtc_list);
+  kms_display->crtcs = crtc_list;
+
+  kms_display->pending_set_crtc = TRUE;
+
+  return TRUE;
+}
+
+void
+meta_renderer_native_set_ignore_crtc (MetaRendererNative *renderer_native,
+                                      uint32_t            id,
+                                      gboolean            ignore)
+{
+  ClutterBackend *clutter_backend = clutter_get_default_backend ();
+  CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend);
+  CoglDisplay *cogl_display = cogl_context_get_display (cogl_context);
+  CoglDisplayEGL *egl_display = cogl_display->winsys;
+  CoglDisplayKMS *kms_display = egl_display->platform;
+  GList *l;
+
+  for (l = kms_display->crtcs; l; l = l->next)
+    {
+      CoglKmsCrtc *crtc = l->data;
+      if (crtc->id == id)
+        {
+          crtc->ignore = ignore;
+          break;
+        }
+    }
+}
+
+static const CoglWinsysVtable *
+get_native_cogl_winsys_vtable (void)
+{
+  static CoglBool vtable_inited = FALSE;
+  static CoglWinsysVtable vtable;
+
+  if (!vtable_inited)
+    {
+      /* The this winsys is a subclass of the EGL winsys so we
+         start by copying its vtable */
+
+      parent_vtable = _cogl_winsys_egl_get_vtable ();
+      vtable = *parent_vtable;
+
+      vtable.id = COGL_WINSYS_ID_CUSTOM;
+      vtable.name = "EGL_KMS";
+
+      vtable.renderer_connect = _cogl_winsys_renderer_connect;
+      vtable.renderer_disconnect = _cogl_winsys_renderer_disconnect;
+
+      vtable.onscreen_init = _cogl_winsys_onscreen_init;
+      vtable.onscreen_deinit = _cogl_winsys_onscreen_deinit;
+
+      /* The KMS winsys doesn't support swap region */
+      vtable.onscreen_swap_region = NULL;
+      vtable.onscreen_swap_buffers_with_damage =
+        _cogl_winsys_onscreen_swap_buffers_with_damage;
+
+      vtable_inited = TRUE;
+    }
+
+  return &vtable;
+}
+
 static CoglRenderer *
 meta_renderer_native_create_cogl_renderer (MetaRenderer *renderer)
 {
-  MetaRendererNative *renderer_native = META_RENDERER_NATIVE (renderer);
   CoglRenderer *cogl_renderer;
 
   cogl_renderer = cogl_renderer_new ();
   cogl_renderer_set_custom_winsys (cogl_renderer,
-                                   _cogl_winsys_egl_kms_get_vtable);
-  cogl_kms_renderer_set_kms_fd (cogl_renderer, renderer_native->kms_fd);
+                                   get_native_cogl_winsys_vtable);
 
   return cogl_renderer;
 }
diff --git a/src/backends/native/meta-renderer-native.h b/src/backends/native/meta-renderer-native.h
index ce6a66f..4ac4cba 100644
--- a/src/backends/native/meta-renderer-native.h
+++ b/src/backends/native/meta-renderer-native.h
@@ -26,12 +26,41 @@
 #define META_RENDERER_NATIVE_H
 
 #include <glib-object.h>
+#include <xf86drmMode.h>
 
 #include "backends/meta-renderer.h"
 
+typedef struct {
+  uint32_t id;
+  uint32_t x, y;
+  drmModeModeInfo mode;
+
+  uint32_t *connectors;
+  uint32_t  count;
+
+  CoglBool  ignore;
+} CoglKmsCrtc;
+
 #define META_TYPE_RENDERER_NATIVE (meta_renderer_native_get_type ())
 G_DECLARE_FINAL_TYPE (MetaRendererNative, meta_renderer_native,
                       META, RENDERER_NATIVE,
                       MetaRenderer)
 
+struct gbm_device * meta_renderer_native_get_gbm (MetaRendererNative *renderer_native);
+
+int meta_renderer_native_get_kms_fd (MetaRendererNative *renderer_native);
+
+void meta_renderer_native_queue_modes_reset (MetaRendererNative *renderer_native);
+
+gboolean meta_renderer_native_set_layout (MetaRendererNative *renderer_native,
+                                          int                 width,
+                                          int                 height,
+                                          CoglKmsCrtc       **crtcs,
+                                          int                 n_crtcs,
+                                          GError            **error);
+
+void meta_renderer_native_set_ignore_crtc (MetaRendererNative *renderer_native,
+                                           uint32_t            id,
+                                           gboolean            ignore);
+
 #endif /* META_RENDERER_NATIVE_H */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]