[gtk/wip/chergert/macos-iosurface] incremental work on cairo rendering to iosurface



commit ca14499f81938b3d323d00809da000ea8b5ed075
Author: Christian Hergert <christian hergert me>
Date:   Wed Feb 9 07:00:23 2022 -0800

    incremental work on cairo rendering to iosurface

 gdk/macos/GdkMacosLayer.c           | 339 ++++++++++++++++++++++++++++++++++++
 gdk/macos/GdkMacosLayer.h           |  43 +++++
 gdk/macos/GdkMacosSuperView.c       |  81 +++++++++
 gdk/macos/GdkMacosSuperView.h       |  38 ++++
 gdk/macos/GdkMacosTile.c            |  56 ++++++
 gdk/macos/GdkMacosTile.h            |  35 ++++
 gdk/macos/GdkMacosWindow.c          |  14 +-
 gdk/macos/GdkMacosWindow.h          |   3 +
 gdk/macos/gdkmacosbuffer-private.h  |  27 ++-
 gdk/macos/gdkmacosbuffer.c          | 169 +++++++++++++++---
 gdk/macos/gdkmacoscairocontext.c    | 188 ++++++++++----------
 gdk/macos/gdkmacossurface-private.h |  10 +-
 gdk/macos/gdkmacossurface.c         |  82 +++++++--
 gdk/macos/meson.build               |   6 +-
 14 files changed, 946 insertions(+), 145 deletions(-)
---
diff --git a/gdk/macos/GdkMacosLayer.c b/gdk/macos/GdkMacosLayer.c
new file mode 100644
index 0000000000..94f3613309
--- /dev/null
+++ b/gdk/macos/GdkMacosLayer.c
@@ -0,0 +1,339 @@
+/* GdkMacosLayer.c
+ *
+ * Copyright © 2022 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#import "GdkMacosLayer.h"
+#import "GdkMacosTile.h"
+
+@implementation GdkMacosLayer
+
+#define TILE_MAX_SIZE 128
+
+typedef struct
+{
+  GdkMacosTile          *tile;
+  cairo_rectangle_int_t  cr_area;
+  CGRect                 area;
+  guint                  opaque : 1;
+} TileInfo;
+
+typedef struct
+{
+  const cairo_region_t *region;
+  guint n_rects;
+  guint iter;
+  cairo_rectangle_int_t rect;
+  cairo_rectangle_int_t stash;
+  guint finished : 1;
+} Tiler;
+
+static void
+tiler_init (Tiler                *tiler,
+            const cairo_region_t *region)
+{
+  memset (tiler, 0, sizeof *tiler);
+
+  if (region == NULL)
+    {
+      tiler->finished = TRUE;
+      return;
+    }
+
+  tiler->region = region;
+  tiler->n_rects = cairo_region_num_rectangles (region);
+
+  if (tiler->n_rects > 0)
+    cairo_region_get_rectangle (region, 0, &tiler->rect);
+  else
+    tiler->finished = TRUE;
+}
+
+static gboolean
+tiler_next (Tiler                 *tiler,
+            cairo_rectangle_int_t *tile)
+{
+  if (tiler->finished)
+    return FALSE;
+
+  if (tiler->rect.width == 0 || tiler->rect.height == 0)
+    {
+      tiler->iter++;
+
+      if (tiler->iter >= tiler->n_rects)
+        {
+          tiler->finished = TRUE;
+          return FALSE;
+        }
+
+      cairo_region_get_rectangle (tiler->region, tiler->iter, &tiler->rect);
+    }
+
+  /* If the next rectangle is too tall, slice the bottom off to
+   * leave just the height we want into tiler->stash.
+   */
+  if (tiler->rect.height > TILE_MAX_SIZE)
+    {
+      tiler->stash = tiler->rect;
+      tiler->stash.y += TILE_MAX_SIZE;
+      tiler->stash.height -= TILE_MAX_SIZE;
+      tiler->rect.height = TILE_MAX_SIZE;
+    }
+
+  /* Now we can take the next horizontal slice */
+  tile->x = tiler->rect.x;
+  tile->y = tiler->rect.y;
+  tile->height = tiler->rect.height;
+  tile->width = MIN (TILE_MAX_SIZE, tiler->rect.width);
+
+  tiler->rect.x += tile->width;
+  tiler->rect.width -= tile->width;
+
+  if (tiler->rect.width == 0)
+    {
+      tiler->rect = tiler->stash;
+      tiler->stash.width = tiler->stash.height = 0;
+    }
+
+  return TRUE;
+}
+
+static inline CGRect
+toCGRect (const cairo_rectangle_int_t *rect)
+{
+  return CGRectMake (rect->x, rect->y, rect->width, rect->height);
+}
+
+static inline cairo_rectangle_int_t
+fromCGRect (const CGRect rect)
+{
+  return (cairo_rectangle_int_t) {
+    rect.origin.x,
+    rect.origin.y,
+    rect.size.width,
+    rect.size.height
+  };
+}
+
+-(id)init
+{
+  self = [super init];
+
+  if (self == NULL)
+    return NULL;
+
+  [self setContentsGravity:kCAGravityCenter];
+  [self setContentsScale:1.0f];
+  [self setGeometryFlipped:YES];
+
+  return self;
+}
+
+-(BOOL)contentsAreFlipped
+{
+  return YES;
+}
+
+-(BOOL)isOpaque
+{
+  return NO;
+}
+
+-(void)_applyLayout:(GArray *)tiles
+{
+  GArray *prev;
+  gboolean exhausted;
+  guint j = 0;
+
+  prev = g_steal_pointer (&self->_tiles);
+  self->_tiles = tiles;
+  exhausted = prev == NULL;
+
+  /* Try to use existing CALayer to avoid creating new layers
+   * as that can be rather expensive.
+   */
+  for (guint i = 0; i < tiles->len; i++)
+    {
+      TileInfo *info = &g_array_index (tiles, TileInfo, i);
+
+      if (!exhausted)
+        {
+          TileInfo *other = NULL;
+
+          for (; j < prev->len; j++)
+            {
+              other = &g_array_index (prev, TileInfo, j);
+
+              if (other->opaque == info->opaque)
+                {
+                  j++;
+                  break;
+                }
+
+              other = NULL;
+            }
+
+          if (other != NULL)
+            {
+              info->tile = g_steal_pointer (&other->tile);
+              [info->tile setFrame:info->area];
+              continue;
+            }
+        }
+
+      info->tile = [GdkMacosTile layer];
+
+      //[info->tile setContentsGravity:kCAGravityCenter];
+      [info->tile setContentsScale:1.0f];
+      [info->tile setOpaque:info->opaque];
+      [info->tile setFrame:info->area];
+
+      [self addSublayer:info->tile];
+    }
+
+  /* Release all of our old layers */
+  if (prev != NULL)
+    {
+      for (guint i = 0; i < prev->len; i++)
+        {
+          TileInfo *info = &g_array_index (prev, TileInfo, i);
+
+          if (info->tile != NULL)
+            [info->tile removeFromSuperlayer];
+        }
+
+      g_array_unref (prev);
+    }
+}
+
+-(void)layoutSublayers
+{
+  Tiler tiler;
+  GArray *ar;
+  cairo_region_t *transparent;
+  cairo_rectangle_int_t rect;
+
+  if (!self->_inSwapBuffer)
+    return;
+
+  ar = g_array_sized_new (FALSE, FALSE, sizeof (TileInfo), 32);
+
+  rect = fromCGRect ([self bounds]);
+  rect.x = rect.y = 0;
+
+  /* Calculate the transparent region (edges usually) */
+  transparent = cairo_region_create_rectangle (&rect);
+  if (self->_opaqueRegion)
+    cairo_region_subtract (transparent, self->_opaqueRegion);
+
+  self->_opaque = cairo_region_is_empty (transparent);
+
+  /* Track transparent children */
+  tiler_init (&tiler, transparent);
+  while (tiler_next (&tiler, &rect))
+    {
+      TileInfo *info;
+
+      g_array_set_size (ar, ar->len+1);
+
+      info = &g_array_index (ar, TileInfo, ar->len-1);
+      info->tile = NULL;
+      info->opaque = FALSE;
+      info->cr_area = rect;
+      info->area = toCGRect (&info->cr_area);
+    }
+
+  /* Track opaque children */
+  tiler_init (&tiler, self->_opaqueRegion);
+  while (tiler_next (&tiler, &rect))
+    {
+      TileInfo *info;
+
+      g_array_set_size (ar, ar->len+1);
+
+      info = &g_array_index (ar, TileInfo, ar->len-1);
+      info->tile = NULL;
+      info->opaque = TRUE;
+      info->cr_area = rect;
+      info->area = toCGRect (&info->cr_area);
+    }
+
+  cairo_region_destroy (transparent);
+
+  [self _applyLayout:g_steal_pointer (&ar)];
+  [super layoutSublayers];
+}
+
+-(void)setFrame:(NSRect)frame
+{
+  if (CGRectEqualToRect (frame, self.frame))
+    return;
+
+  self->_layoutInvalid = TRUE;
+
+  [super setFrame:frame];
+}
+
+-(void)setOpaqueRegion:(const cairo_region_t *)opaqueRegion
+{
+  g_clear_pointer (&self->_opaqueRegion, cairo_region_destroy);
+  self->_opaqueRegion = cairo_region_copy (opaqueRegion);
+  self->_layoutInvalid = TRUE;
+}
+
+-(void)swapBuffer:(GdkMacosBuffer *)buffer withDamage:(const cairo_region_t *)damage
+{
+  IOSurfaceRef ioSurface = _gdk_macos_buffer_get_native (buffer);
+  double scale = _gdk_macos_buffer_get_device_scale (buffer);
+  size_t width = _gdk_macos_buffer_get_width (buffer) / scale;
+  size_t height = _gdk_macos_buffer_get_height (buffer) / scale;
+
+  if (self->_layoutInvalid)
+    {
+      self->_layoutInvalid = FALSE;
+
+      self->_inSwapBuffer = TRUE;
+      [self layoutSublayers];
+      self->_inSwapBuffer = FALSE;
+    }
+
+  if (self->_tiles == NULL)
+    return;
+
+  for (guint i = 0; i < self->_tiles->len; i++)
+    {
+      const TileInfo *info = &g_array_index (self->_tiles, TileInfo, i);
+      cairo_region_overlap_t overlap;
+      CGRect area;
+
+      overlap = cairo_region_contains_rectangle (damage, &info->cr_area);
+      if (overlap == CAIRO_REGION_OVERLAP_OUT)
+        continue;
+
+      area.origin.x = info->area.origin.x / (double)width;
+      area.origin.y = info->area.origin.y / (double)height;
+      area.size.width = info->area.size.width / (double)width;
+      area.size.height = info->area.size.height / (double)height;
+
+      [info->tile swapBuffer:ioSurface withRect:area];
+    }
+}
+
+@end
diff --git a/gdk/macos/GdkMacosLayer.h b/gdk/macos/GdkMacosLayer.h
new file mode 100644
index 0000000000..18b7d9afae
--- /dev/null
+++ b/gdk/macos/GdkMacosLayer.h
@@ -0,0 +1,43 @@
+/* GdkMacosLayer.h
+ *
+ * Copyright © 2022 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include <QuartzCore/QuartzCore.h>
+#include <IOSurface/IOSurface.h>
+
+#include <cairo.h>
+#include <glib.h>
+
+#include "gdkmacosbuffer-private.h"
+
+#define GDK_IS_MACOS_LAYER(obj) ((obj) && [obj isKindOfClass:[GdkMacosLayer class]])
+
+@interface GdkMacosLayer : CALayer
+{
+  cairo_region_t *_opaqueRegion;
+  GArray         *_tiles;
+  guint           _opaque : 1;
+  guint           _layoutInvalid : 1;
+  guint           _inSwapBuffer : 1;
+};
+
+-(void)setOpaqueRegion:(const cairo_region_t *)opaqueRegion;
+-(void)swapBuffer:(GdkMacosBuffer *)buffer withDamage:(const cairo_region_t *)damage;
+
+@end
diff --git a/gdk/macos/GdkMacosSuperView.c b/gdk/macos/GdkMacosSuperView.c
new file mode 100644
index 0000000000..04a0420477
--- /dev/null
+++ b/gdk/macos/GdkMacosSuperView.c
@@ -0,0 +1,81 @@
+/* GdkMacosSuperView.c
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include <CoreGraphics/CoreGraphics.h>
+
+#import "GdkMacosLayer.h"
+#import "GdkMacosSuperView.h"
+
+@implementation GdkMacosSuperView
+
+-(id)initWithFrame:(NSRect)frame
+{
+  if ((self = [super initWithFrame:frame]))
+    {
+      GdkMacosLayer *layer = [GdkMacosLayer layer];
+
+      [self setLayerContentsRedrawPolicy:NSViewLayerContentsRedrawNever];
+      [self setLayer:layer];
+      [self setWantsLayer:YES];
+    }
+
+  return self;
+}
+
+-(BOOL)isFlipped
+{
+  return YES;
+}
+
+-(BOOL)acceptsFirstMouse
+{
+  return YES;
+}
+
+-(BOOL)mouseDownCanMoveWindow
+{
+  return NO;
+}
+
+-(void)setFrame:(NSRect)rect
+{
+  [super setFrame:rect];
+  self->_nextFrameDirty = TRUE;
+}
+
+-(void)setOpaqueRegion:(const cairo_region_t *)opaqueRegion
+{
+  [(GdkMacosLayer *)[self layer] setOpaqueRegion:opaqueRegion];
+}
+
+-(void)swapBuffer:(GdkMacosBuffer *)buffer withDamage:(const cairo_region_t *)damage
+{
+  if (self->_nextFrameDirty)
+    {
+      self->_nextFrameDirty = FALSE;
+      [[self layer] setFrame:[self frame]];
+    }
+
+  [(GdkMacosLayer *)[self layer] swapBuffer:buffer withDamage:damage];
+}
+
+@end
diff --git a/gdk/macos/GdkMacosSuperView.h b/gdk/macos/GdkMacosSuperView.h
new file mode 100644
index 0000000000..a0a2a198f5
--- /dev/null
+++ b/gdk/macos/GdkMacosSuperView.h
@@ -0,0 +1,38 @@
+/* GdkMacosSuperView.h
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include <cairo.h>
+
+#import "GdkMacosBaseView.h"
+
+#include "gdkmacosbuffer-private.h"
+
+#define GDK_IS_MACOS_SUPER_VIEW(obj) ((obj) && [obj isKindOfClass:[GdkMacosSuperView class]])
+
+@interface GdkMacosSuperView : GdkMacosBaseView
+{
+  NSRect _nextFrame;
+  guint _nextFrameDirty : 1;
+}
+
+-(void)setOpaqueRegion:(const cairo_region_t *)opaqueRegion;
+-(void)swapBuffer:(GdkMacosBuffer *)buffer withDamage:(const cairo_region_t *)damage;
+
+@end
diff --git a/gdk/macos/GdkMacosTile.c b/gdk/macos/GdkMacosTile.c
new file mode 100644
index 0000000000..09e82542c2
--- /dev/null
+++ b/gdk/macos/GdkMacosTile.c
@@ -0,0 +1,56 @@
+/* GdkMacosTile.c
+ *
+ * Copyright © 2022 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#import "GdkMacosTile.h"
+
+@implementation GdkMacosTile
+
+-(BOOL)contentsAreFlipped
+{
+  return YES;
+}
+
+-(BOOL)isOpaque
+{
+  return self->_opaque;
+}
+
+-(void)setOpaque:(BOOL)opaque
+{
+  self->_opaque = opaque;
+}
+
+-(void)swapBuffer:(IOSurfaceRef)buffer withRect:(CGRect)rect
+{
+  CGRect prevRect = [self contentsRect];
+
+  self.contents = nil;
+  self.contents = (id)buffer;
+
+  if (!CGRectEqualToRect (prevRect, rect))
+    self.contentsRect = rect;
+
+  if (self.contentsScale != 1.0f)
+    self.contentsScale = 1.0f;
+}
+
+@end
diff --git a/gdk/macos/GdkMacosTile.h b/gdk/macos/GdkMacosTile.h
new file mode 100644
index 0000000000..09295800fb
--- /dev/null
+++ b/gdk/macos/GdkMacosTile.h
@@ -0,0 +1,35 @@
+/* GdkMacosTile.h
+ *
+ * Copyright © 2022 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include <QuartzCore/QuartzCore.h>
+
+#include "gdkmacosbuffer-private.h"
+
+#define GDK_IS_MACOS_TILE(obj) ((obj) && [obj isKindOfClass:[GdkMacosTile class]])
+
+@interface GdkMacosTile : CALayer
+{
+  guint _opaque : 1;
+};
+
+-(void)setOpaque:(BOOL)opaque;
+-(void)swapBuffer:(IOSurfaceRef)buffer withRect:(CGRect)rect;
+
+@end
diff --git a/gdk/macos/GdkMacosWindow.c b/gdk/macos/GdkMacosWindow.c
index 8dda79ab4d..bb78c9fe87 100644
--- a/gdk/macos/GdkMacosWindow.c
+++ b/gdk/macos/GdkMacosWindow.c
@@ -24,8 +24,8 @@
 #include <gdk/gdk.h>
 
 #import "GdkMacosBaseView.h"
-#import "GdkMacosCairoView.h"
 #import "GdkMacosGLView.h"
+#import "GdkMacosSuperView.h"
 #import "GdkMacosWindow.h"
 
 #include "gdkmacosclipboard-private.h"
@@ -266,7 +266,7 @@ typedef NSString *CALayerContentsGravity;
 
   [[self contentView] setFrame:NSMakeRect (0, 0, surface->width, surface->height)];
 
-  _gdk_surface_update_size (surface);
+  _gdk_macos_surface_update_size (gdk_surface);
 
   gdk_surface_request_layout (surface);
 
@@ -281,7 +281,7 @@ typedef NSString *CALayerContentsGravity;
                    defer:(BOOL)flag
                   screen:(NSScreen *)screen
 {
-  GdkMacosCairoView *view;
+  GdkMacosSuperView *view;
 
   self = [super initWithContentRect:contentRect
                                styleMask:styleMask
@@ -292,8 +292,9 @@ typedef NSString *CALayerContentsGravity;
   [self setAcceptsMouseMovedEvents:YES];
   [self setDelegate:(id<NSWindowDelegate>)self];
   [self setReleasedWhenClosed:YES];
+  [self setPreservesContentDuringLiveResize:NO];
 
-  view = [[GdkMacosCairoView alloc] initWithFrame:contentRect];
+  view = [[GdkMacosSuperView alloc] initWithFrame:contentRect];
   [self setContentView:view];
   [view release];
 
@@ -852,4 +853,9 @@ typedef NSString *CALayerContentsGravity;
   return NO;
 }
 
+-(void)swapBuffer:(GdkMacosBuffer *)buffer withDamage:(const cairo_region_t *)damage
+{
+  [(GdkMacosSuperView *)[self contentView] swapBuffer:buffer withDamage:damage];
+}
+
 @end
diff --git a/gdk/macos/GdkMacosWindow.h b/gdk/macos/GdkMacosWindow.h
index 61f546a78b..cb8b2efad1 100644
--- a/gdk/macos/GdkMacosWindow.h
+++ b/gdk/macos/GdkMacosWindow.h
@@ -21,9 +21,11 @@
 
 #import <AppKit/AppKit.h>
 #import <Foundation/Foundation.h>
+#import <IOSurface/IOSurface.h>
 
 #include <gdk/gdk.h>
 
+#include "gdkmacosbuffer-private.h"
 #include "gdkmacosdisplay.h"
 #include "gdkmacossurface.h"
 #include "edgesnapping.h"
@@ -66,5 +68,6 @@
 -(BOOL)trackManualMove;
 -(BOOL)trackManualResize;
 -(void)setDecorated:(BOOL)decorated;
+-(void)swapBuffer:(GdkMacosBuffer *)buffer withDamage:(const cairo_region_t *)damage;
 
 @end
diff --git a/gdk/macos/gdkmacosbuffer-private.h b/gdk/macos/gdkmacosbuffer-private.h
index a7663d8556..ac8ec4e107 100644
--- a/gdk/macos/gdkmacosbuffer-private.h
+++ b/gdk/macos/gdkmacosbuffer-private.h
@@ -20,6 +20,10 @@
 #ifndef __GDK_MACOS_BUFFER_PRIVATE_H__
 #define __GDK_MACOS_BUFFER_PRIVATE_H__
 
+#include <CoreGraphics/CoreGraphics.h>
+#include <Foundation/Foundation.h>
+#include <IOSurface/IOSurface.h>
+
 #include "gdkmacosdisplay.h"
 
 G_BEGIN_DECLS
@@ -28,12 +32,23 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (GdkMacosBuffer, gdk_macos_buffer, GDK, MACOS_BUFFER, GObject)
 
-GdkMacosBuffer *_gdk_macos_buffer_new    (GdkMacosDisplay *display,
-                                          int              width,
-                                          int              height,
-                                          int              bytes_per_pixel);
-void            _gdk_macos_buffer_lock   (GdkMacosBuffer *self);
-void            _gdk_macos_buffer_unlock (GdkMacosBuffer *self);
+GdkMacosBuffer       *_gdk_macos_buffer_new              (GdkMacosDisplay *display,
+                                                          int              width,
+                                                          int              height,
+                                                          double           device_scale,
+                                                          int              bytes_per_element,
+                                                          int              bits_per_pixel);
+IOSurfaceRef          _gdk_macos_buffer_get_native       (GdkMacosBuffer  *self);
+CGContextRef          _gdk_macos_buffer_create_context   (GdkMacosBuffer  *self);
+void                  _gdk_macos_buffer_lock             (GdkMacosBuffer  *self);
+void                  _gdk_macos_buffer_unlock           (GdkMacosBuffer  *self);
+guint                 _gdk_macos_buffer_get_width        (GdkMacosBuffer  *self);
+guint                 _gdk_macos_buffer_get_height       (GdkMacosBuffer  *self);
+guint                 _gdk_macos_buffer_get_stride       (GdkMacosBuffer  *self);
+double                _gdk_macos_buffer_get_device_scale (GdkMacosBuffer  *self);
+const cairo_region_t *_gdk_macos_buffer_get_damage       (GdkMacosBuffer  *self);
+void                  _gdk_macos_buffer_set_damage       (GdkMacosBuffer  *self,
+                                                          cairo_region_t  *damage);
 
 G_END_DECLS
 
diff --git a/gdk/macos/gdkmacosbuffer.c b/gdk/macos/gdkmacosbuffer.c
index 8584478c24..7e66b83a09 100644
--- a/gdk/macos/gdkmacosbuffer.c
+++ b/gdk/macos/gdkmacosbuffer.c
@@ -26,10 +26,28 @@
 
 #include "gdkmacosbuffer-private.h"
 
+CGContextRef CGIOSurfaceContextCreate      (IOSurfaceRef    io_surface,
+                                            size_t          width,
+                                            size_t          height,
+                                            size_t          bitsPerComponent,
+                                            size_t          bitsPerPixel,
+                                            CGColorSpaceRef colorSpace,
+                                            CGBitmapInfo    bitmapInfo);
+CGImageRef   CGIOSurfaceContextCreateImage (CGContextRef    cgContext);
+
 struct _GdkMacosBuffer
 {
-  GObject parent_instance;
-  IOSurfaceRef surface;
+  GObject          parent_instance;
+  cairo_region_t  *damage;
+  CGColorSpaceRef  colorspace;
+  IOSurfaceRef     surface;
+  int              lock_count;
+  guint            bytes_per_element;
+  guint            bits_per_pixel;
+  guint            width;
+  guint            height;
+  guint            stride;
+  double           device_scale;
 };
 
 G_DEFINE_TYPE (GdkMacosBuffer, gdk_macos_buffer, G_TYPE_OBJECT)
@@ -38,12 +56,14 @@ static void
 gdk_macos_buffer_dispose (GObject *object)
 {
   GdkMacosBuffer *self = (GdkMacosBuffer *)object;
+
+  if (self->lock_count != 0)
+    g_critical ("Attempt to dispose %s while lock is held",
+                G_OBJECT_TYPE_NAME (self));
   
-  if (self->surface != NULL)
-    {
-      CFRelease (self->surface);
-      self->surface = NULL;
-    }
+  g_clear_pointer (&self->surface, CFRelease);
+  g_clear_pointer (&self->colorspace, CGColorSpaceRelease);
+  g_clear_pointer (&self->damage, cairo_region_destroy);
   
   G_OBJECT_CLASS (gdk_macos_buffer_parent_class)->dispose (object);
 }
@@ -72,9 +92,10 @@ add_int (CFMutableDictionaryRef dict,
 }
 
 static IOSurfaceRef
-create_surface (int width,
-                int height,
-                int bytes_per_pixel)
+create_surface (int   width,
+                int   height,
+                int   bytes_per_element,
+                guint *stride)
 {
   CFMutableDictionaryRef props;
   IOSurfaceRef ret;
@@ -88,22 +109,22 @@ create_surface (int width,
   if (props == NULL)
     return NULL;
 
-  add_int (props, kIOSurfaceWidth, width);
-  add_int (props, kIOSurfaceHeight, height);
-  add_int (props, kIOSurfaceBytesPerElement, bytes_per_pixel);
-  
-  bytes_per_row = IOSurfaceAlignProperty (kIOSurfaceBytesPerRow, width * bytes_per_pixel);
-  add_int (props, kIOSurfaceBytesPerRow, bytes_per_row);
-  
+  bytes_per_row = IOSurfaceAlignProperty (kIOSurfaceBytesPerRow, width * bytes_per_element);
   total_bytes = IOSurfaceAlignProperty (kIOSurfaceAllocSize, height * bytes_per_row);
+
   add_int (props, kIOSurfaceAllocSize, total_bytes);
-  
-  add_int (props, kIOSurfacePixelFormat, (int)kCVPixelFormatType_32BGRA);
+  add_int (props, kIOSurfaceBytesPerElement, bytes_per_element);
+  add_int (props, kIOSurfaceBytesPerRow, bytes_per_row);
+  add_int (props, kIOSurfaceHeight, height);
+  add_int (props, kIOSurfacePixelFormat, (int)'BGRA');
+  add_int (props, kIOSurfaceWidth, width);
 
   ret = IOSurfaceCreate (props);
 
   CFRelease (props);
 
+  *stride = bytes_per_row;
+
   return ret;
 }
 
@@ -111,32 +132,130 @@ GdkMacosBuffer *
 _gdk_macos_buffer_new (GdkMacosDisplay *display,
                        int              width,
                        int              height,
-                       int              bytes_per_pixel)
+                       double           device_scale,
+                       int              bytes_per_element,
+                       int              bits_per_pixel)
 {
   GdkMacosBuffer *self;
 
   g_return_val_if_fail (GDK_IS_MACOS_DISPLAY (display), NULL);
+  g_return_val_if_fail (width > 0, NULL);
+  g_return_val_if_fail (height > 0, NULL);
 
   self = g_object_new (GDK_TYPE_MACOS_BUFFER, NULL);
-  self->surface = create_surface (width, height, bytes_per_pixel);
+  self->colorspace = CGColorSpaceCreateDeviceRGB ();
+  self->bytes_per_element = bytes_per_element;
+  self->bits_per_pixel = bits_per_pixel;
+  self->surface = create_surface (width, height, bytes_per_element, &self->stride);
+  self->width = width;
+  self->height = height;
+  self->device_scale = device_scale;
+  self->lock_count = 0;
 
   if (self->surface == NULL)
-    {
-      g_object_unref (self);
-      return NULL;
-    }
+    g_clear_object (&self);
 
   return self;
 }
 
+IOSurfaceRef
+_gdk_macos_buffer_get_native (GdkMacosBuffer *self)
+{
+  g_return_val_if_fail (GDK_IS_MACOS_BUFFER (self), NULL);
+
+  return self->surface;
+}
+
 void
 _gdk_macos_buffer_lock (GdkMacosBuffer *self)
 {
   g_return_if_fail (GDK_IS_MACOS_BUFFER (self));
+  g_return_if_fail (self->lock_count == 0);
+
+  self->lock_count++;
+
+  IOSurfaceLock (self->surface, 0, NULL);
 }
 
 void
 _gdk_macos_buffer_unlock (GdkMacosBuffer *self)
 {
   g_return_if_fail (GDK_IS_MACOS_BUFFER (self));
+  g_return_if_fail (self->lock_count == 1);
+
+  self->lock_count--;
+
+  IOSurfaceUnlock (self->surface, 0, NULL);
+}
+
+CGContextRef
+_gdk_macos_buffer_create_context (GdkMacosBuffer *self)
+{
+  CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
+  CGContextRef cg_context;
+
+  g_return_val_if_fail (GDK_IS_MACOS_BUFFER (self), NULL);
+
+  cg_context = CGIOSurfaceContextCreate (self->surface,
+                                         self->width,
+                                         self->height,
+                                         self->bits_per_pixel / 4,
+                                         self->bits_per_pixel,
+                                         self->colorspace,
+                                         bitmapInfo);
+
+  return cg_context;
+}
+
+guint
+_gdk_macos_buffer_get_width (GdkMacosBuffer *self)
+{
+  g_return_val_if_fail (GDK_IS_MACOS_BUFFER (self), 0);
+
+  return self->width;
+}
+
+guint
+_gdk_macos_buffer_get_height (GdkMacosBuffer *self)
+{
+  g_return_val_if_fail (GDK_IS_MACOS_BUFFER (self), 0);
+
+  return self->height;
+}
+
+guint
+_gdk_macos_buffer_get_stride (GdkMacosBuffer *self)
+{
+  g_return_val_if_fail (GDK_IS_MACOS_BUFFER (self), 0);
+
+  return self->stride;
+}
+
+double
+_gdk_macos_buffer_get_device_scale (GdkMacosBuffer *self)
+{
+  g_return_val_if_fail (GDK_IS_MACOS_BUFFER (self), 1.0);
+
+  return self->device_scale;
+}
+
+const cairo_region_t *
+_gdk_macos_buffer_get_damage (GdkMacosBuffer *self)
+{
+  g_return_val_if_fail (GDK_IS_MACOS_BUFFER (self), NULL);
+
+  return self->damage;
+}
+
+void
+_gdk_macos_buffer_set_damage (GdkMacosBuffer *self,
+                              cairo_region_t *damage)
+{
+  g_return_if_fail (GDK_IS_MACOS_BUFFER (self));
+
+  if (damage == self->damage)
+    return;
+
+  g_clear_pointer (&self->damage, cairo_region_destroy);
+  self->damage = damage;
 }
diff --git a/gdk/macos/gdkmacoscairocontext.c b/gdk/macos/gdkmacoscairocontext.c
index c1dd7f677c..adb5a5994e 100644
--- a/gdk/macos/gdkmacoscairocontext.c
+++ b/gdk/macos/gdkmacoscairocontext.c
@@ -22,19 +22,19 @@
 
 #include "gdkconfig.h"
 
+#include <cairo.h>
+#include <QuartzCore/QuartzCore.h>
 #include <CoreGraphics/CoreGraphics.h>
 
-#import "GdkMacosCairoView.h"
+#import "GdkMacosSuperView.h"
 
+#include "gdkmacosbuffer-private.h"
 #include "gdkmacoscairocontext-private.h"
 #include "gdkmacossurface-private.h"
 
 struct _GdkMacosCairoContext
 {
-  GdkCairoContext  parent_instance;
-
-  cairo_surface_t *window_surface;
-  cairo_t         *cr;
+  GdkCairoContext parent_instance;
 };
 
 struct _GdkMacosCairoContextClass
@@ -44,80 +44,97 @@ struct _GdkMacosCairoContextClass
 
 G_DEFINE_TYPE (GdkMacosCairoContext, _gdk_macos_cairo_context, GDK_TYPE_CAIRO_CONTEXT)
 
-static cairo_surface_t *
-create_cairo_surface_for_surface (GdkSurface *surface)
+static void
+unlock_buffer (gpointer data)
 {
-  static const cairo_user_data_key_t buffer_key;
-  cairo_surface_t *cairo_surface;
-  guint8 *data;
-  cairo_format_t format;
-  size_t size;
-  size_t rowstride;
-  size_t width;
-  size_t height;
-  int scale;
-
-  g_assert (GDK_IS_MACOS_SURFACE (surface));
-
-  /* We use a cairo image surface here instead of a quartz surface because
-   * we get strange artifacts with the quartz surface such as empty
-   * cross-fades when hovering buttons. For performance, we want to be using
-   * GL rendering so there isn't much point here as correctness is better.
-   *
-   * Additionally, so we can take avantage of faster paths in Core
-   * Graphics, we want our data pointer to be 16-byte aligned and our rows
-   * to be 16-byte aligned or we risk errors below us. Normally, cairo
-   * image surface does not guarantee the later, which means we could end
-   * up doing some costly copies along the way to compositing.
-   */
-
-  if ([GDK_MACOS_SURFACE (surface)->window isOpaque])
-    format = CAIRO_FORMAT_RGB24;
-  else
-    format = CAIRO_FORMAT_ARGB32;
-
-  scale = gdk_surface_get_scale_factor (surface);
-  width = scale * gdk_surface_get_width (surface);
-  height = scale * gdk_surface_get_height (surface);
-  rowstride = (cairo_format_stride_for_width (format, width) + 0xF) & ~0xF;
-  size = rowstride * height;
-  data = g_malloc0 (size);
-  cairo_surface = cairo_image_surface_create_for_data (data, format, width, height, rowstride);
-  cairo_surface_set_user_data (cairo_surface, &buffer_key, data, g_free);
-  cairo_surface_set_device_scale (cairo_surface, scale, scale);
-
-  return cairo_surface;
+  GdkMacosBuffer *buffer = data;
+
+  g_assert (GDK_IS_MACOS_BUFFER (buffer));
+
+  _gdk_macos_buffer_unlock (buffer);
+  g_clear_object (&buffer);
 }
 
 static cairo_t *
-do_cairo_create (GdkMacosCairoContext *self)
+_gdk_macos_cairo_context_cairo_create (GdkCairoContext *cairo_context)
 {
+  GdkMacosCairoContext *self = (GdkMacosCairoContext *)cairo_context;
+  static const cairo_user_data_key_t buffer_key;
+  const cairo_region_t *damage;
+  cairo_surface_t *image_surface;
+  GdkMacosBuffer *back;
+  IOSurfaceRef io_surface;
   GdkSurface *surface;
+  gpointer base_address;
+  NSWindow *nswindow;
   cairo_t *cr;
+  double scale;
+  guint width;
+  guint height;
+  guint stride;
+  gboolean opaque;
+  double x, y, x2, y2;
 
   g_assert (GDK_IS_MACOS_CAIRO_CONTEXT (self));
 
   surface = gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (self));
-  cr = cairo_create (self->window_surface);
 
-  /* Draw upside down as quartz prefers */
-  cairo_translate (cr, 0, surface->height);
-  cairo_scale (cr, 1.0, -1.0);
+  nswindow = _gdk_macos_surface_get_native (GDK_MACOS_SURFACE (surface));
+  opaque = [nswindow isOpaque];
 
-  return cr;
-}
+  back = _gdk_macos_surface_get_back_buffer (GDK_MACOS_SURFACE (surface));
+  io_surface = _gdk_macos_buffer_get_native (back);
+  damage = _gdk_macos_buffer_get_damage (back);
+  width = _gdk_macos_buffer_get_width (back);
+  height = _gdk_macos_buffer_get_height (back);
+  scale = _gdk_macos_buffer_get_device_scale (back);
+  stride = _gdk_macos_buffer_get_stride (back);
+  base_address = IOSurfaceGetBaseAddress (io_surface);
 
-static cairo_t *
-_gdk_macos_cairo_context_cairo_create (GdkCairoContext *cairo_context)
-{
-  GdkMacosCairoContext *self = (GdkMacosCairoContext *)cairo_context;
+  image_surface = cairo_image_surface_create_for_data (base_address,
+                                                       opaque ? CAIRO_FORMAT_RGB24 : CAIRO_FORMAT_ARGB32,
+                                                       width, height, stride);
+  cairo_surface_set_device_scale (image_surface, scale, scale);
 
-  g_assert (GDK_IS_MACOS_CAIRO_CONTEXT (self));
+  _gdk_macos_buffer_lock (back);
+  cairo_surface_set_user_data (image_surface,
+                               &buffer_key,
+                               g_object_ref (back),
+                               unlock_buffer);
+
+  /* TODO: Copy from other image back to this one */
 
-  if (self->cr != NULL)
-    return cairo_reference (self->cr);
+  cr = cairo_create (image_surface);
+
+  if (cr != NULL && damage != NULL)
+    {
+      gdk_cairo_region (cr, damage);
+      cairo_clip (cr);
+    }
+
+  if (!opaque)
+    {
+      cairo_region_t *transparent;
 
-  return do_cairo_create (self);
+      cairo_save (cr);
+
+      cairo_clip_extents (cr, &x, &y, &x2, &y2);
+      transparent = cairo_region_create_rectangle (&(cairo_rectangle_int_t) { x, y, x2-x, y2-y });
+      if (surface->opaque_region)
+        cairo_region_subtract (transparent, surface->opaque_region);
+
+      gdk_cairo_region (cr, transparent);
+      cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
+      cairo_fill (cr);
+
+      cairo_region_destroy (transparent);
+
+      cairo_restore (cr);
+    }
+
+  cairo_surface_destroy (image_surface);
+
+  return cr;
 }
 
 static void
@@ -126,49 +143,38 @@ _gdk_macos_cairo_context_begin_frame (GdkDrawContext *draw_context,
                                       cairo_region_t *region)
 {
   GdkMacosCairoContext *self = (GdkMacosCairoContext *)draw_context;
+  const cairo_region_t *front_damage;
+  GdkMacosBuffer *back;
+  GdkMacosBuffer *front;
   GdkSurface *surface;
-  NSWindow *nswindow;
 
   g_assert (GDK_IS_MACOS_CAIRO_CONTEXT (self));
 
-  surface = gdk_draw_context_get_surface (draw_context);
-  nswindow = _gdk_macos_surface_get_native (GDK_MACOS_SURFACE (surface));
+  [CATransaction begin];
+  [CATransaction setDisableActions:YES];
 
-  if (self->window_surface == NULL)
-    self->window_surface = create_cairo_surface_for_surface (surface);
+  surface = gdk_draw_context_get_surface (draw_context);
+  back = _gdk_macos_surface_get_back_buffer (GDK_MACOS_SURFACE (surface));
+  front = _gdk_macos_surface_get_back_buffer (GDK_MACOS_SURFACE (surface));
 
-  self->cr = do_cairo_create (self);
+  if ((front_damage = _gdk_macos_buffer_get_damage (front)))
+    cairo_region_union (region, front_damage);
 
-  if (![nswindow isOpaque])
-    {
-      cairo_save (self->cr);
-      gdk_cairo_region (self->cr, region);
-      cairo_set_source_rgba (self->cr, 0, 0, 0, 0);
-      cairo_set_operator (self->cr, CAIRO_OPERATOR_SOURCE);
-      cairo_fill (self->cr);
-      cairo_restore (self->cr);
-    }
+  _gdk_macos_buffer_set_damage (back, cairo_region_copy (region));
 }
 
 static void
 _gdk_macos_cairo_context_end_frame (GdkDrawContext *draw_context,
                                     cairo_region_t *painted)
 {
-  GdkMacosCairoContext *self = (GdkMacosCairoContext *)draw_context;
   GdkSurface *surface;
-  NSView *nsview;
 
-  g_assert (GDK_IS_MACOS_CAIRO_CONTEXT (self));
-  g_assert (self->window_surface != NULL);
+  g_assert (GDK_IS_MACOS_CAIRO_CONTEXT (draw_context));
 
   surface = gdk_draw_context_get_surface (draw_context);
-  nsview = _gdk_macos_surface_get_view (GDK_MACOS_SURFACE (surface));
-
-  g_clear_pointer (&self->cr, cairo_destroy);
+  _gdk_macos_surface_swap_buffers (GDK_MACOS_SURFACE (surface), painted);
 
-  if (GDK_IS_MACOS_CAIRO_VIEW (nsview))
-    [(GdkMacosCairoView *)nsview setCairoSurface:self->window_surface
-                                      withDamage:painted];
+  [CATransaction commit];
 }
 
 static void
@@ -177,26 +183,20 @@ _gdk_macos_cairo_context_surface_resized (GdkDrawContext *draw_context)
   GdkMacosCairoContext *self = (GdkMacosCairoContext *)draw_context;
 
   g_assert (GDK_IS_MACOS_CAIRO_CONTEXT (self));
-
-  g_clear_pointer (&self->window_surface, cairo_surface_destroy);
 }
 
 static void
 _gdk_macos_cairo_context_dispose (GObject *object)
 {
-  GdkMacosCairoContext *self = (GdkMacosCairoContext *)object;
-
-  g_clear_pointer (&self->window_surface, cairo_surface_destroy);
-
   G_OBJECT_CLASS (_gdk_macos_cairo_context_parent_class)->dispose (object);
 }
 
 static void
 _gdk_macos_cairo_context_class_init (GdkMacosCairoContextClass *klass)
 {
-  GObjectClass *object_class = G_OBJECT_CLASS (klass);
   GdkCairoContextClass *cairo_context_class = GDK_CAIRO_CONTEXT_CLASS (klass);
   GdkDrawContextClass *draw_context_class = GDK_DRAW_CONTEXT_CLASS (klass);
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
   object_class->dispose = _gdk_macos_cairo_context_dispose;
 
diff --git a/gdk/macos/gdkmacossurface-private.h b/gdk/macos/gdkmacossurface-private.h
index 76cf853c01..e73f2437f6 100644
--- a/gdk/macos/gdkmacossurface-private.h
+++ b/gdk/macos/gdkmacossurface-private.h
@@ -41,8 +41,8 @@ struct _GdkMacosSurface
 {
   GdkSurface parent_instance;
 
-  GdkMacosBuffer *back;
-  GdkMacosBuffer *front;
+  GdkMacosBuffer *buffers[2];
+  GdkMacosBuffer *front_buffer;
 
   GList main;
   GList sorted;
@@ -66,6 +66,7 @@ struct _GdkMacosSurface
 
   guint did_initial_present : 1;
   guint geometry_dirty : 1;
+  guint back_buffer : 1;
 };
 
 struct _GdkMacosSurfaceClass
@@ -135,6 +136,11 @@ void               _gdk_macos_surface_set_opacity             (GdkMacosSurface
 void               _gdk_macos_surface_get_root_coords         (GdkMacosSurface    *self,
                                                                int                *x,
                                                                int                *y);
+void               _gdk_macos_surface_update_size             (GdkMacosSurface      *self);
+GdkMacosBuffer    *_gdk_macos_surface_get_back_buffer         (GdkMacosSurface      *self);
+GdkMacosBuffer    *_gdk_macos_surface_get_front_buffer        (GdkMacosSurface      *self);
+void               _gdk_macos_surface_swap_buffers            (GdkMacosSurface      *self,
+                                                               const cairo_region_t *damage);
 
 G_END_DECLS
 
diff --git a/gdk/macos/gdkmacossurface.c b/gdk/macos/gdkmacossurface.c
index 26e73faad4..5cc9c3c47a 100644
--- a/gdk/macos/gdkmacossurface.c
+++ b/gdk/macos/gdkmacossurface.c
@@ -23,7 +23,7 @@
 #include <float.h>
 #include <gdk/gdk.h>
 
-#import "GdkMacosCairoView.h"
+#import "GdkMacosSuperView.h"
 
 #include "gdkmacossurface-private.h"
 
@@ -112,8 +112,8 @@ gdk_macos_surface_set_opaque_region (GdkSurface     *surface,
     }
 
   if ((nsview = _gdk_macos_surface_get_view (GDK_MACOS_SURFACE (surface))) &&
-      GDK_IS_MACOS_CAIRO_VIEW (nsview))
-    [(GdkMacosCairoView *)nsview setOpaqueRegion:region];
+      GDK_IS_MACOS_SUPER_VIEW (nsview))
+    [(GdkMacosSuperView *)nsview setOpaqueRegion:region];
 }
 
 static void
@@ -864,20 +864,35 @@ _gdk_macos_surface_synthesize_null_key (GdkMacosSurface *self)
 static void
 gdk_macos_surface_update_buffers (GdkMacosSurface *self)
 {
-  GdkMacosBuffer *front;
-  GdkDisplay *display;
+  GdkMacosDisplay *display;
+  double scale;
+  guint width;
+  guint height;
 
   g_assert (GDK_IS_MACOS_SURFACE (self));
 
-  display = gdk_surface_get_display (GDK_SURFACE (self));
-  front = _gdk_macos_buffer_new (GDK_MACOS_DISPLAY (display),
-                                 GDK_SURFACE (self)->width,
-                                 GDK_SURFACE (self)->height,
-                                 4);
+  scale = gdk_surface_get_scale_factor (GDK_SURFACE (self));
+  width = GDK_SURFACE (self)->width * scale;
+  height = GDK_SURFACE (self)->height * scale;
+
+  g_print ("Creating new buffers: %dx%d\n", width, height);
 
-  g_print ("Front: %p\n", front);
+  display = GDK_MACOS_DISPLAY (gdk_surface_get_display (GDK_SURFACE (self)));
+
+  for (guint i = 0; i < G_N_ELEMENTS (self->buffers); i++)
+    {
+      GdkMacosBuffer *buffer = _gdk_macos_buffer_new (display, width, height, scale, 4, 32);
+
+      if (buffer != NULL)
+        {
+          g_print ("%p replaced with %p\n", self->buffers[i], buffer);
+          g_clear_object (&self->buffers[i]);
+          self->buffers[i] = buffer;
+        }
+    }
 
-  g_object_unref (front);
+  self->front_buffer = self->buffers[0];
+  self->back_buffer = 1;
 }
 
 void
@@ -1017,7 +1032,7 @@ _gdk_macos_surface_monitor_changed (GdkMacosSurface *self)
       g_object_unref (monitor);
     }
 
-  _gdk_surface_update_size (GDK_SURFACE (self));
+  _gdk_macos_surface_update_size (self);
   gdk_surface_invalidate_rect (GDK_SURFACE (self), NULL);
 }
 
@@ -1099,3 +1114,44 @@ _gdk_macos_surface_get_root_coords (GdkMacosSurface *self,
   if (y)
     *y = out_y;
 }
+
+void
+_gdk_macos_surface_update_size (GdkMacosSurface *self)
+{
+  g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
+
+  _gdk_surface_update_size (GDK_SURFACE (self));
+  gdk_macos_surface_update_buffers (self);
+}
+
+GdkMacosBuffer *
+_gdk_macos_surface_get_back_buffer (GdkMacosSurface *self)
+{
+  g_return_val_if_fail (GDK_IS_MACOS_SURFACE (self), NULL);
+
+  return self->buffers[self->back_buffer];
+}
+
+GdkMacosBuffer *
+_gdk_macos_surface_get_front_buffer (GdkMacosSurface *self)
+{
+  g_return_val_if_fail (GDK_IS_MACOS_SURFACE (self), NULL);
+
+  return self->front_buffer;
+}
+
+void
+_gdk_macos_surface_swap_buffers (GdkMacosSurface      *self,
+                                 const cairo_region_t *damage)
+{
+  GdkMacosBuffer *buffer;
+
+  g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
+  g_return_if_fail (damage != NULL);
+
+  buffer = self->buffers[self->back_buffer];
+  self->front_buffer = buffer;
+  //self->back_buffer = (self->back_buffer + 1) % G_N_ELEMENTS (self->buffers);
+
+  [self->window swapBuffer:buffer withDamage:damage];
+}
diff --git a/gdk/macos/meson.build b/gdk/macos/meson.build
index e6e8dd798f..a7f15b4557 100644
--- a/gdk/macos/meson.build
+++ b/gdk/macos/meson.build
@@ -23,10 +23,14 @@ gdk_macos_sources = files([
   'gdkmacostoplevelsurface.c',
 
   'GdkMacosBaseView.c',
+  'GdkMacosLayer.c',
+  'GdkMacosTile.c',
+  'GdkMacosSuperView.c',
+  'GdkMacosWindow.c',
+
   'GdkMacosCairoView.c',
   'GdkMacosCairoSubview.c',
   'GdkMacosGLView.c',
-  'GdkMacosWindow.c',
 ])
 
 gdk_macos_public_headers = files([


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