diff --git a/gdk/gdkglcontext.c b/gdk/gdkglcontext.c
index 373f2fefd5..be514c0ac6 100644
--- a/gdk/gdkglcontext.c
+++ b/gdk/gdkglcontext.c
@@ -648,6 +648,12 @@ gdk_gl_context_surface_resized (GdkDrawContext *draw_context)
gdk_gl_context_clear_old_updated_area (context);
}
+static guint
+gdk_gl_context_real_get_default_framebuffer (GdkGLContext *self)
+{
+ return 0;
+}
+
static void
gdk_gl_context_class_init (GdkGLContextClass *klass)
{
@@ -659,6 +665,7 @@ gdk_gl_context_class_init (GdkGLContextClass *klass)
klass->is_shared = gdk_gl_context_real_is_shared;
klass->make_current = gdk_gl_context_real_make_current;
klass->clear_current = gdk_gl_context_real_clear_current;
+ klass->get_default_framebuffer = gdk_gl_context_real_get_default_framebuffer;
draw_context_class->begin_frame = gdk_gl_context_real_begin_frame;
draw_context_class->end_frame = gdk_gl_context_real_end_frame;
diff --git a/gdk/gdkglcontextprivate.h b/gdk/gdkglcontextprivate.h
index ff11349b3e..0a3739f090 100644
--- a/gdk/gdkglcontextprivate.h
+++ b/gdk/gdkglcontextprivate.h
@@ -71,6 +71,8 @@ struct _GdkGLContextClass
gboolean (* is_shared) (GdkGLContext *self,
GdkGLContext *other);
+
+ guint (* get_default_framebuffer) (GdkGLContext *self);
};
typedef struct {
diff --git a/gdk/macos/GdkMacosCairoSubview.c b/gdk/macos/GdkMacosCairoSubview.c
deleted file mode 100644
index fddb0ed0c0..0000000000
--- a/gdk/macos/GdkMacosCairoSubview.c
+++ /dev/null
@@ -1,167 +0,0 @@
-/* GdkMacosCairoSubview.c
- *
- * Copyright © 2020 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 .
- *
- * SPDX-License-Identifier: LGPL-2.1-or-later
- */
-
-#include "config.h"
-
-#include
-#include
-
-#import "GdkMacosCairoSubview.h"
-#import "GdkMacosCairoView.h"
-
-#include "gdkmacossurface-private.h"
-
-@implementation GdkMacosCairoSubview
-
--(void)dealloc
-{
- g_clear_pointer (&self->clip, g_array_unref);
- g_clear_pointer (&self->damage, g_array_unref);
- g_clear_pointer (&self->image, CGImageRelease);
- [super dealloc];
-}
-
--(BOOL)isOpaque
-{
- return _isOpaque;
-}
-
--(BOOL)isFlipped
-{
- return YES;
-}
-
--(GdkSurface *)gdkSurface
-{
- return GDK_SURFACE ([(GdkMacosBaseView *)[self superview] gdkSurface]);
-}
-
--(void)drawRect:(NSRect)rect
-{
- CGContextRef cgContext;
- NSView *root_view;
- CGRect image_rect;
- CGRect abs_bounds;
- CGSize scale;
- int abs_height;
-
- if (self->image == NULL)
- return;
-
- cgContext = [[NSGraphicsContext currentContext] CGContext];
- root_view = [[self window] contentView];
- abs_bounds = [self convertRect:[self bounds] toView:root_view];
- abs_height = CGImageGetHeight (self->image);
-
- CGContextSaveGState (cgContext);
-
- /* Clip while our context is still using matching coordinates
- * to the self->clip region. This is usually just on the views
- * for the shadow areas.
- */
- CGContextAddRect (cgContext, [self bounds]);
- if (self->clip != NULL)
- {
- for (guint i = 0; i < self->clip->len; i++)
- CGContextAddRect (cgContext, g_array_index (self->clip, CGRect, i));
- }
- if (self->damage != NULL)
- {
- for (guint i = 0; i < self->damage->len; i++)
- CGContextAddRect (cgContext, g_array_index (self->damage, CGRect, i));
- }
- CGContextClip (cgContext);
-
- /* Scale/Translate so that the CGImageRef draws in proper format/placement */
- scale = CGSizeMake (1.0, 1.0);
- scale = CGContextConvertSizeToDeviceSpace (cgContext, scale);
- CGContextScaleCTM (cgContext, 1.0 / scale.width, 1.0 / scale.height);
- CGContextTranslateCTM (cgContext, -abs_bounds.origin.x, -abs_bounds.origin.y);
- image_rect = CGRectMake (-abs_bounds.origin.x,
- -abs_bounds.origin.y,
- CGImageGetWidth (self->image),
- CGImageGetHeight (self->image));
- CGContextDrawImage (cgContext, image_rect, self->image);
-
- CGContextRestoreGState (cgContext);
-}
-
--(void)setImage:(CGImageRef)theImage
- withDamage:(cairo_region_t *)region
-{
- if (theImage != image)
- {
- g_clear_pointer (&image, CGImageRelease);
- if (theImage)
- image = CGImageRetain (theImage);
- }
-
- [self convertRegion:region toArray:&self->damage andDisplay:YES];
-
- for (id view in [self subviews])
- [(GdkMacosCairoSubview *)view setImage:theImage withDamage:region];
-}
-
--(void)setOpaque:(BOOL)opaque
-{
- self->_isOpaque = opaque;
-}
-
--(void)convertRegion:(const cairo_region_t *)region
- toArray:(GArray **)array
- andDisplay:(BOOL)display
-{
- NSView *root_view;
- CGRect abs_bounds;
- guint n_rects;
-
- if (*array == NULL)
- *array = g_array_new (FALSE, FALSE, sizeof (CGRect));
- else
- g_array_set_size (*array, 0);
-
- root_view = [[self window] contentView];
- abs_bounds = [self convertRect:[self bounds] toView:root_view];
- n_rects = cairo_region_num_rectangles (region);
-
- for (guint i = 0; i < n_rects; i++)
- {
- cairo_rectangle_int_t rect;
- CGRect nsrect;
-
- cairo_region_get_rectangle (region, i, &rect);
- nsrect = CGRectIntersection (abs_bounds, CGRectMake (rect.x, rect.y, rect.width, rect.height));
-
- if (!CGRectIsNull (nsrect))
- g_array_append_val (*array, nsrect);
-
- if (display)
- [self setNeedsDisplayInRect:CGRectMake (rect.x - abs_bounds.origin.x,
- rect.y - abs_bounds.origin.y,
- rect.width, rect.height)];
- }
-}
-
--(void)setClip:(cairo_region_t*)region
-{
- [self convertRegion:region toArray:&self->clip andDisplay:NO];
-}
-
-@end
diff --git a/gdk/macos/GdkMacosCairoView.c b/gdk/macos/GdkMacosCairoView.c
deleted file mode 100644
index 6596702fe8..0000000000
--- a/gdk/macos/GdkMacosCairoView.c
+++ /dev/null
@@ -1,267 +0,0 @@
-/* GdkMacosCairoView.c
- *
- * Copyright © 2020 Red Hat, Inc.
- * Copyright © 2005-2007 Imendio AB
- *
- * 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 .
- *
- * SPDX-License-Identifier: LGPL-2.1-or-later
- */
-
-#include "config.h"
-
-#include
-#include
-
-
-#import "GdkMacosCairoView.h"
-#import "GdkMacosCairoSubview.h"
-
-#include "gdkmacosmonitor-private.h"
-#include "gdkmacossurface-private.h"
-
-@implementation GdkMacosCairoView
-
--(void)dealloc
-{
- g_clear_pointer (&self->opaque, g_ptr_array_unref);
- self->transparent = NULL;
-
- [super dealloc];
-}
-
--(BOOL)isOpaque
-{
- if ([self window])
- return [[self window] isOpaque];
- return YES;
-}
-
--(BOOL)isFlipped
-{
- return YES;
-}
-
--(void)setNeedsDisplay:(BOOL)needsDisplay
-{
- for (id child in [self subviews])
- [child setNeedsDisplay:needsDisplay];
-}
-
-static void
-release_surface_provider (void *info,
- const void *data,
- size_t size)
-{
- cairo_surface_destroy (info);
-}
-
--(void)setCairoSurface:(cairo_surface_t *)cairoSurface
- withDamage:(cairo_region_t *)cairoRegion
-{
- CGImageRef image = NULL;
-
- if (cairoSurface != NULL)
- {
- const CGColorRenderingIntent intent = kCGRenderingIntentDefault;
- CGDataProviderRef provider;
- CGColorSpaceRef rgb;
- cairo_format_t format;
- CGBitmapInfo bitmap = kCGBitmapByteOrder32Host;
- GdkMonitor *monitor;
- guint8 *framebuffer;
- size_t width;
- size_t height;
- int rowstride;
- int bpp;
- int bpc;
-
- cairo_surface_flush (cairoSurface);
-
- format = cairo_image_surface_get_format (cairoSurface);
- framebuffer = cairo_image_surface_get_data (cairoSurface);
- rowstride = cairo_image_surface_get_stride (cairoSurface);
- width = cairo_image_surface_get_width (cairoSurface);
- height = cairo_image_surface_get_height (cairoSurface);
- monitor = _gdk_macos_surface_get_best_monitor ([self gdkSurface]);
- rgb = _gdk_macos_monitor_copy_colorspace (GDK_MACOS_MONITOR (monitor));
-
- /* If we have an WCG colorspace, just take the slow path or we risk
- * really screwing things up.
- */
- if (CGColorSpaceIsWideGamutRGB (rgb))
- {
- CGColorSpaceRelease (rgb);
- rgb = CGColorSpaceCreateDeviceRGB ();
- }
-
- /* Assert that our image surface was created correctly with
- * 16-byte aligned pointers and strides. This is needed to
- * ensure that we're working with fast paths in CoreGraphics.
- */
- g_assert (format == CAIRO_FORMAT_ARGB32 || format == CAIRO_FORMAT_RGB24);
- g_assert (framebuffer != NULL);
- g_assert (((intptr_t)framebuffer & (intptr_t)~0xF) == (intptr_t)framebuffer);
- g_assert ((rowstride & ~0xF) == rowstride);
-
- if (format == CAIRO_FORMAT_ARGB32)
- {
- bitmap |= kCGImageAlphaPremultipliedFirst;
- bpp = 32;
- bpc = 8;
- }
- else
- {
- bitmap |= kCGImageAlphaNoneSkipFirst;
- bpp = 32;
- bpc = 8;
- }
-
- provider = CGDataProviderCreateWithData (cairo_surface_reference (cairoSurface),
- framebuffer,
- rowstride * height,
- release_surface_provider);
-
- image = CGImageCreate (width, height, bpc, bpp, rowstride, rgb, bitmap, provider, NULL, FALSE, intent);
-
- CGDataProviderRelease (provider);
- CGColorSpaceRelease (rgb);
- }
-
- for (id view in [self subviews])
- [(GdkMacosCairoSubview *)view setImage:image
- withDamage:cairoRegion];
-
- if (image != NULL)
- CGImageRelease (image);
-}
-
--(void)removeOpaqueChildren
-{
- [[self->transparent subviews]
- makeObjectsPerformSelector:@selector(removeFromSuperview)];
-
- if (self->opaque->len)
- g_ptr_array_remove_range (self->opaque, 0, self->opaque->len);
-}
-
--(void)setOpaqueRegion:(cairo_region_t *)region
-{
- cairo_region_t *transparent_clip;
- NSRect abs_bounds;
- guint n_rects;
-
- if (region == NULL)
- return;
-
- abs_bounds = [self convertRect:[self bounds] toView:nil];
- n_rects = cairo_region_num_rectangles (region);
-
- /* First, we create a clip region for the transparent region to use so that
- * we dont end up exposing too much other than the corners on CSD.
- */
- transparent_clip = cairo_region_create_rectangle (&(cairo_rectangle_int_t) {
- abs_bounds.origin.x, abs_bounds.origin.y,
- abs_bounds.size.width, abs_bounds.size.height
- });
- cairo_region_subtract (transparent_clip, region);
- [(GdkMacosCairoSubview *)self->transparent setClip:transparent_clip];
- cairo_region_destroy (transparent_clip);
-
- /* The common case (at least for opaque windows and CSD) is that we will
- * have either one or two opaque rectangles. If we detect that the same
- * number of them are available as the previous, we can just resize the
- * previous ones to avoid adding/removing views at a fast rate while
- * resizing.
- */
- if (n_rects == self->opaque->len)
- {
- for (guint i = 0; i < n_rects; i++)
- {
- GdkMacosCairoSubview *child;
- cairo_rectangle_int_t rect;
-
- child = g_ptr_array_index (self->opaque, i);
- cairo_region_get_rectangle (region, i, &rect);
-
- [child setFrame:NSMakeRect (rect.x - abs_bounds.origin.x,
- rect.y - abs_bounds.origin.y,
- rect.width,
- rect.height)];
- }
-
- return;
- }
-
- [self removeOpaqueChildren];
- for (guint i = 0; i < n_rects; i++)
- {
- GdkMacosCairoSubview *child;
- cairo_rectangle_int_t rect;
- NSRect nsrect;
-
- cairo_region_get_rectangle (region, i, &rect);
- nsrect = NSMakeRect (rect.x - abs_bounds.origin.x,
- rect.y - abs_bounds.origin.y,
- rect.width,
- rect.height);
-
- child = [[GdkMacosCairoSubview alloc] initWithFrame:nsrect];
- [child setOpaque:YES];
- [child setWantsLayer:YES];
- [self->transparent addSubview:child];
- g_ptr_array_add (self->opaque, child);
- }
-}
-
--(NSView *)initWithFrame:(NSRect)frame
-{
- if ((self = [super initWithFrame:frame]))
- {
- /* An array to track all the opaque children placed into
- * the child self->transparent. This allows us to reuse them
- * when we receive a new opaque area instead of discarding
- * them on each draw.
- */
- self->opaque = g_ptr_array_new ();
-
- /* Setup our primary subview which will render all content that is not
- * within an opaque region (such as shadows for CSD windows). For opaque
- * windows, this will all be obscurred by other views, so it doesn't
- * matter much to have it here.
- */
- self->transparent = [[GdkMacosCairoSubview alloc] initWithFrame:frame];
- [self addSubview:self->transparent];
- }
-
- return self;
-}
-
--(void)setFrame:(NSRect)rect
-{
- [super setFrame:rect];
- [self->transparent setFrame:NSMakeRect (0, 0, rect.size.width, rect.size.height)];
-}
-
--(BOOL)acceptsFirstMouse
-{
- return YES;
-}
-
--(BOOL)mouseDownCanMoveWindow
-{
- return NO;
-}
-
-@end
diff --git a/gdk/macos/GdkMacosGLView.c b/gdk/macos/GdkMacosGLView.c
deleted file mode 100644
index d7f6437451..0000000000
--- a/gdk/macos/GdkMacosGLView.c
+++ /dev/null
@@ -1,125 +0,0 @@
-/* GdkMacosGLView.c
- *
- * Copyright 2020 Christian Hergert
- *
- * 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 .
- *
- * SPDX-License-Identifier: LGPL-2.1-or-later
- */
-
-#include "config.h"
-
-#include
-#include
-
-#include "gdkmacossurface-private.h"
-
-#import "GdkMacosGLView.h"
-
-@implementation GdkMacosGLView
-
-G_GNUC_BEGIN_IGNORE_DEPRECATIONS
-
--(void)lockFocus
-{
- NSOpenGLContext *context;
-
- [super lockFocus];
-
- context = [self openGLContext];
-
- if ([context view] != self)
- [context setView: self];
-}
-
--(void)drawRect:(NSRect)rect
-{
-}
-
--(void)clearGLContext
-{
- if (_openGLContext != nil)
- [_openGLContext clearDrawable];
-
- _openGLContext = nil;
-}
-
--(void)setOpenGLContext:(NSOpenGLContext*)context
-{
- if (_openGLContext != context)
- {
- if (_openGLContext != nil)
- [_openGLContext clearDrawable];
-
- _openGLContext = context;
-
- if (_openGLContext != nil)
- {
- [_openGLContext setView:self];
- [self setWantsLayer:YES];
- [self.layer setContentsGravity:kCAGravityBottomLeft];
- [_openGLContext update];
- }
- }
-}
-
--(NSOpenGLContext *)openGLContext
-{
- return _openGLContext;
-}
-
--(BOOL)isOpaque
-{
- if ([self window])
- return [[self window] isOpaque];
- return YES;
-}
-
--(BOOL)isFlipped
-{
- return YES;
-}
-
--(BOOL)acceptsFirstMouse
-{
- return YES;
-}
-
--(BOOL)mouseDownCanMoveWindow
-{
- return NO;
-}
-
--(void)invalidateRegion:(const cairo_region_t *)region
-{
- if (region != NULL)
- {
- guint n_rects = cairo_region_num_rectangles (region);
-
- for (guint i = 0; i < n_rects; i++)
- {
- cairo_rectangle_int_t rect;
- NSRect nsrect;
-
- cairo_region_get_rectangle (region, i, &rect);
- nsrect = NSMakeRect (rect.x, rect.y, rect.width, rect.height);
-
- [self setNeedsDisplayInRect:nsrect];
- }
- }
-}
-
-G_GNUC_END_IGNORE_DEPRECATIONS
-
-@end
diff --git a/gdk/macos/GdkMacosLayer.c b/gdk/macos/GdkMacosLayer.c
new file mode 100644
index 0000000000..4de47a5658
--- /dev/null
+++ b/gdk/macos/GdkMacosLayer.c
@@ -0,0 +1,386 @@
+/* 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 .
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#import "GdkMacosLayer.h"
+#import "GdkMacosTile.h"
+
+@protocol CanSetContentsOpaque
+- (void)setContentsOpaque:(BOOL)mask;
+@end
+
+@implementation GdkMacosLayer
+
+#define TILE_MAX_SIZE 128
+#define TILE_EDGE_MAX_SIZE 512
+
+static CGAffineTransform flipTransform;
+static gboolean hasFlipTransform;
+
+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,
+ int max_size)
+{
+ 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 > max_size)
+ {
+ tiler->stash = tiler->rect;
+ tiler->stash.y += max_size;
+ tiler->stash.height -= max_size;
+ tiler->rect.height = 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 (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
+{
+ if (!hasFlipTransform)
+ {
+ hasFlipTransform = TRUE;
+ flipTransform = CGAffineTransformMakeScale (1, -1);
+ }
+
+ self = [super init];
+
+ if (self == NULL)
+ return NULL;
+
+ self->_layoutInvalid = TRUE;
+
+ [self setContentsGravity:kCAGravityCenter];
+ [self setContentsScale:1.0f];
+ [self setGeometryFlipped:YES];
+
+ return self;
+}
+
+-(BOOL)isOpaque
+{
+ return NO;
+}
+
+-(void)_applyLayout:(GArray *)tiles
+{
+ CGAffineTransform transform;
+ GArray *prev;
+ gboolean exhausted;
+ guint j = 0;
+
+ if (self->_isFlipped)
+ transform = flipTransform;
+ else
+ transform = CGAffineTransformIdentity;
+
+ 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];
+ [info->tile setAffineTransform:transform];
+ continue;
+ }
+ }
+
+ info->tile = [GdkMacosTile layer];
+
+ [info->tile setAffineTransform:transform];
+ [info->tile setContentsScale:1.0f];
+ [info->tile setOpaque:info->opaque];
+ [(id)info->tile setContentsOpaque: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;
+ int max_size;
+
+ if (!self->_inSwapBuffer)
+ return;
+
+ self->_layoutInvalid = FALSE;
+
+ 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);
+
+ /* If we have transparent borders around the opaque region, then
+ * we are okay with a bit larger tiles since they don't change
+ * all that much and are generally small in width.
+ */
+ if (!self->_opaque &&
+ self->_opaqueRegion &&
+ !cairo_region_is_empty (self->_opaqueRegion))
+ max_size = TILE_EDGE_MAX_SIZE;
+ else
+ max_size = TILE_MAX_SIZE;
+
+ /* Track transparent children */
+ tiler_init (&tiler, transparent);
+ while (tiler_next (&tiler, &rect, max_size))
+ {
+ 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, TILE_MAX_SIZE))
+ {
+ 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 (frame.size.width != self.bounds.size.width ||
+ frame.size.height != self.bounds.size.height)
+ {
+ self->_layoutInvalid = TRUE;
+ [self setNeedsLayout];
+ }
+
+ [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;
+
+ [self setNeedsLayout];
+}
+
+-(void)swapBuffer:(GdkMacosBuffer *)buffer withDamage:(const cairo_region_t *)damage
+{
+ IOSurfaceRef ioSurface = _gdk_macos_buffer_get_native (buffer);
+ gboolean flipped = _gdk_macos_buffer_get_flipped (buffer);
+ double scale = _gdk_macos_buffer_get_device_scale (buffer);
+ double width = _gdk_macos_buffer_get_width (buffer) / scale;
+ double height = _gdk_macos_buffer_get_height (buffer) / scale;
+
+ if (flipped != self->_isFlipped)
+ {
+ self->_isFlipped = flipped;
+ self->_layoutInvalid = TRUE;
+ }
+
+ if (self->_layoutInvalid)
+ {
+ 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 / width;
+ area.size.width = info->area.size.width / width;
+ area.size.height = info->area.size.height / height;
+
+ if (flipped)
+ area.origin.y = (height - info->area.origin.y - info->area.size.height) / height;
+ else
+ area.origin.y = info->area.origin.y / 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..74ba14ff86
--- /dev/null
+++ b/gdk/macos/GdkMacosLayer.h
@@ -0,0 +1,44 @@
+/* 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 .
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include
+#include
+
+#include
+#include
+
+#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;
+ guint _isFlipped : 1;
+};
+
+-(void)setOpaqueRegion:(const cairo_region_t *)opaqueRegion;
+-(void)swapBuffer:(GdkMacosBuffer *)buffer withDamage:(const cairo_region_t *)damage;
+
+@end
diff --git a/gdk/macos/GdkMacosGLView.h b/gdk/macos/GdkMacosTile.c
similarity index 54%
rename from gdk/macos/GdkMacosGLView.h
rename to gdk/macos/GdkMacosTile.c
index 320b1a163b..737c719e0d 100644
--- a/gdk/macos/GdkMacosGLView.h
+++ b/gdk/macos/GdkMacosTile.c
@@ -1,7 +1,6 @@
-/* GdkMacosGLView.h
+/* GdkMacosTile.c
*
- * Copyright © 2020 Red Hat, Inc.
- * Copyright © 2005-2007 Imendio AB
+ * 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
@@ -19,23 +18,34 @@
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
-#include
+#include "config.h"
-#import "GdkMacosBaseView.h"
+#include
-#define GDK_IS_MACOS_GL_VIEW(obj) ((obj) && [obj isKindOfClass:[GdkMacosGLView class]])
+#import "GdkMacosTile.h"
-G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+@implementation GdkMacosTile
-@interface GdkMacosGLView : GdkMacosBaseView
+-(id)init
{
- NSOpenGLContext *_openGLContext;
+ self = [super init];
+
+ [self setContentsScale:1.0];
+ [self setEdgeAntialiasingMask:0];
+ [self setDrawsAsynchronously:YES];
+
+ return self;
}
--(void)setOpenGLContext:(NSOpenGLContext*)context;
--(NSOpenGLContext *)openGLContext;
--(void)invalidateRegion:(const cairo_region_t *)region;
+-(void)swapBuffer:(IOSurfaceRef)buffer withRect:(CGRect)rect
+{
+ if G_LIKELY ([self contents] == (id)buffer)
+ [(id)self setContentsChanged];
+ else
+ [self setContents:(id)buffer];
-G_GNUC_END_IGNORE_DEPRECATIONS
+ if G_UNLIKELY (!CGRectEqualToRect ([self contentsRect], rect))
+ self.contentsRect = rect;
+}
@end
diff --git a/gdk/macos/GdkMacosCairoSubview.h b/gdk/macos/GdkMacosTile.h
similarity index 59%
rename from gdk/macos/GdkMacosCairoSubview.h
rename to gdk/macos/GdkMacosTile.h
index e03b47727d..ada07ebcf3 100644
--- a/gdk/macos/GdkMacosCairoSubview.h
+++ b/gdk/macos/GdkMacosTile.h
@@ -1,6 +1,6 @@
-/* GdkMacosCairoSubview.h
+/* GdkMacosTile.h
*
- * Copyright © 2020 Red Hat, Inc.
+ * 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
@@ -18,22 +18,20 @@
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
-#include
-#include
-#include
+#include
-#define GDK_IS_MACOS_CAIRO_SUBVIEW(obj) ((obj) && [obj isKindOfClass:[GdkMacosCairoSubview class]])
+#include "gdkmacosbuffer-private.h"
-@interface GdkMacosCairoSubview : NSView
+#define GDK_IS_MACOS_TILE(obj) ((obj) && [obj isKindOfClass:[GdkMacosTile class]])
+
+@protocol CanSetContentsChanged
+-(void)setContentsChanged;
+@end
+
+@interface GdkMacosTile : CALayer
{
- BOOL _isOpaque;
- GArray *clip;
- GArray *damage;
- CGImageRef image;
-}
+};
--(void)setOpaque:(BOOL)opaque;
--(void)setImage:(CGImageRef)theImage withDamage:(cairo_region_t *)region;
--(void)setClip:(cairo_region_t*)region;
+-(void)swapBuffer:(IOSurfaceRef)buffer withRect:(CGRect)rect;
@end
diff --git a/gdk/macos/GdkMacosView.c b/gdk/macos/GdkMacosView.c
new file mode 100644
index 0000000000..52a55a7cef
--- /dev/null
+++ b/gdk/macos/GdkMacosView.c
@@ -0,0 +1,86 @@
+/* GdkMacosView.c
+ *
+ * Copyright 2022 Christian Hergert
+ *
+ * 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 .
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include
+
+#import "GdkMacosLayer.h"
+#import "GdkMacosView.h"
+
+@implementation GdkMacosView
+
+-(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];
+}
+
+-(BOOL)wantsUpdateLayer
+{
+ return YES;
+}
+
+-(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/GdkMacosCairoView.h b/gdk/macos/GdkMacosView.h
similarity index 64%
rename from gdk/macos/GdkMacosCairoView.h
rename to gdk/macos/GdkMacosView.h
index 1c28d83b39..db3b05efa0 100644
--- a/gdk/macos/GdkMacosCairoView.h
+++ b/gdk/macos/GdkMacosView.h
@@ -1,7 +1,6 @@
-/* GdkMacosCairoView.h
+/* GdkMacosView.h
*
- * Copyright © 2020 Red Hat, Inc.
- * Copyright © 2005-2007 Imendio AB
+ * Copyright 2022 Christian Hergert
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -23,15 +22,17 @@
#import "GdkMacosBaseView.h"
-#define GDK_IS_MACOS_CAIRO_VIEW(obj) ((obj) && [obj isKindOfClass:[GdkMacosCairoView class]])
+#include "gdkmacosbuffer-private.h"
-@interface GdkMacosCairoView : GdkMacosBaseView
+#define GDK_IS_MACOS_VIEW(obj) ((obj) && [obj isKindOfClass:[GdkMacosView class]])
+
+@interface GdkMacosView : GdkMacosBaseView
{
- NSView *transparent;
- GPtrArray *opaque;
+ NSRect _nextFrame;
+ guint _nextFrameDirty : 1;
}
--(void)setCairoSurface:(cairo_surface_t *)cairoSurface
- withDamage:(cairo_region_t *)region;
+-(void)setOpaqueRegion:(const cairo_region_t *)opaqueRegion;
+-(void)swapBuffer:(GdkMacosBuffer *)buffer withDamage:(const cairo_region_t *)damage;
@end
diff --git a/gdk/macos/GdkMacosWindow.c b/gdk/macos/GdkMacosWindow.c
index 3f45bc8d4c..f879decbc9 100644
--- a/gdk/macos/GdkMacosWindow.c
+++ b/gdk/macos/GdkMacosWindow.c
@@ -24,8 +24,7 @@
#include
#import "GdkMacosBaseView.h"
-#import "GdkMacosCairoView.h"
-#import "GdkMacosGLView.h"
+#import "GdkMacosView.h"
#import "GdkMacosWindow.h"
#include "gdkmacosclipboard-private.h"
@@ -150,8 +149,7 @@ typedef NSString *CALayerContentsGravity;
_gdk_macos_display_break_all_grabs (GDK_MACOS_DISPLAY (display), time);
/* Reset gravity */
- if (GDK_IS_MACOS_GL_VIEW ([self contentView]))
- [[[self contentView] layer] setContentsGravity:kCAGravityBottomLeft];
+ [[[self contentView] layer] setContentsGravity:kCAGravityBottomLeft];
break;
}
@@ -225,7 +223,7 @@ typedef NSString *CALayerContentsGravity;
defer:(BOOL)flag
screen:(NSScreen *)screen
{
- GdkMacosCairoView *view;
+ GdkMacosView *view;
self = [super initWithContentRect:contentRect
styleMask:styleMask
@@ -236,8 +234,9 @@ typedef NSString *CALayerContentsGravity;
[self setAcceptsMouseMovedEvents:YES];
[self setDelegate:(id)self];
[self setReleasedWhenClosed:YES];
+ [self setPreservesContentDuringLiveResize:NO];
- view = [[GdkMacosCairoView alloc] initWithFrame:contentRect];
+ view = [[GdkMacosView alloc] initWithFrame:contentRect];
[self setContentView:view];
[view release];
@@ -754,7 +753,7 @@ typedef NSString *CALayerContentsGravity;
-(void)windowWillExitFullScreen:(NSNotification *)aNotification
{
- [self setFrame:lastUnfullscreenFrame display:YES];
+ [self setFrame:lastUnfullscreenFrame display:NO];
}
-(void)windowDidExitFullScreen:(NSNotification *)aNotification
@@ -810,4 +809,9 @@ typedef NSString *CALayerContentsGravity;
return NO;
}
+-(void)swapBuffer:(GdkMacosBuffer *)buffer withDamage:(const cairo_region_t *)damage
+{
+ [(GdkMacosView *)[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
#import
+#import
#include
+#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
new file mode 100644
index 0000000000..6be201147c
--- /dev/null
+++ b/gdk/macos/gdkmacosbuffer-private.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright © 2021 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.1 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 .
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef __GDK_MACOS_BUFFER_PRIVATE_H__
+#define __GDK_MACOS_BUFFER_PRIVATE_H__
+
+#include
+#include
+#include
+
+#include
+#include
+
+G_BEGIN_DECLS
+
+#define GDK_TYPE_MACOS_BUFFER (gdk_macos_buffer_get_type())
+
+G_DECLARE_FINAL_TYPE (GdkMacosBuffer, gdk_macos_buffer, GDK, MACOS_BUFFER, GObject)
+
+GdkMacosBuffer *_gdk_macos_buffer_new (int width,
+ int height,
+ double device_scale,
+ int bytes_per_element,
+ int bits_per_pixel);
+IOSurfaceRef _gdk_macos_buffer_get_native (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);
+gpointer _gdk_macos_buffer_get_data (GdkMacosBuffer *self);
+gboolean _gdk_macos_buffer_get_flipped (GdkMacosBuffer *self);
+void _gdk_macos_buffer_set_flipped (GdkMacosBuffer *self,
+ gboolean flipped);
+
+G_END_DECLS
+
+#endif /* __GDK_MACOS_BUFFER_PRIVATE_H__ */
diff --git a/gdk/macos/gdkmacosbuffer.c b/gdk/macos/gdkmacosbuffer.c
new file mode 100644
index 0000000000..ac99302ee4
--- /dev/null
+++ b/gdk/macos/gdkmacosbuffer.c
@@ -0,0 +1,271 @@
+/*
+ * Copyright © 2021 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.1 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 .
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+
+#include "gdkmacosbuffer-private.h"
+
+struct _GdkMacosBuffer
+{
+ GObject parent_instance;
+ cairo_region_t *damage;
+ IOSurfaceRef surface;
+ int lock_count;
+ guint bytes_per_element;
+ guint bits_per_pixel;
+ guint width;
+ guint height;
+ guint stride;
+ double device_scale;
+ guint flipped : 1;
+};
+
+G_DEFINE_TYPE (GdkMacosBuffer, gdk_macos_buffer, G_TYPE_OBJECT)
+
+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));
+
+ /* We could potentially force the unload of our surface here with
+ * IOSurfaceSetPurgeable (self->surface, kIOSurfacePurgeableEmpty, NULL)
+ * but that would cause it to empty when the layers may still be attached
+ * to it. Better to just let it get GC'd by the system after they have
+ * moved on to a new buffer.
+ */
+ g_clear_pointer (&self->surface, CFRelease);
+ g_clear_pointer (&self->damage, cairo_region_destroy);
+
+ G_OBJECT_CLASS (gdk_macos_buffer_parent_class)->dispose (object);
+}
+
+static void
+gdk_macos_buffer_class_init (GdkMacosBufferClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = gdk_macos_buffer_dispose;
+}
+
+static void
+gdk_macos_buffer_init (GdkMacosBuffer *self)
+{
+}
+
+static void
+add_int (CFMutableDictionaryRef dict,
+ const CFStringRef key,
+ int value)
+{
+ CFNumberRef number = CFNumberCreate (NULL, kCFNumberIntType, &value);
+ CFDictionaryAddValue (dict, key, number);
+ CFRelease (number);
+}
+
+static IOSurfaceRef
+create_surface (int width,
+ int height,
+ int bytes_per_element,
+ guint *stride)
+{
+ CFMutableDictionaryRef props;
+ IOSurfaceRef ret;
+ size_t bytes_per_row;
+ size_t total_bytes;
+
+ props = CFDictionaryCreateMutable (kCFAllocatorDefault,
+ 16,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ if (props == NULL)
+ return NULL;
+
+ 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, 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;
+}
+
+GdkMacosBuffer *
+_gdk_macos_buffer_new (int width,
+ int height,
+ double device_scale,
+ int bytes_per_element,
+ int bits_per_pixel)
+{
+ GdkMacosBuffer *self;
+
+ 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->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_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;
+}
+
+/**
+ * _gdk_macos_buffer_lock:
+ *
+ * This function matches the IOSurfaceLock() name but what it really
+ * does is page the buffer back for the CPU to access from VRAM.
+ *
+ * Generally we don't want to do that, but we do need to in some
+ * cases such as when we are rendering with Cairo. There might
+ * be an opportunity later to avoid that, but since we are using
+ * GL pretty much everywhere already, we don't try.
+ */
+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);
+}
+
+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 = cairo_region_reference (damage);
+}
+
+gpointer
+_gdk_macos_buffer_get_data (GdkMacosBuffer *self)
+{
+ g_return_val_if_fail (GDK_IS_MACOS_BUFFER (self), NULL);
+
+ return IOSurfaceGetBaseAddress (self->surface);
+}
+
+gboolean
+_gdk_macos_buffer_get_flipped (GdkMacosBuffer *self)
+{
+ g_return_val_if_fail (GDK_IS_MACOS_BUFFER (self), FALSE);
+
+ return self->flipped;
+}
+
+void
+_gdk_macos_buffer_set_flipped (GdkMacosBuffer *self,
+ gboolean flipped)
+{
+ g_return_if_fail (GDK_IS_MACOS_BUFFER (self));
+
+ self->flipped = !!flipped;
+}
diff --git a/gdk/macos/gdkmacoscairocontext.c b/gdk/macos/gdkmacoscairocontext.c
index c1dd7f677c..041f1193e6 100644
--- a/gdk/macos/gdkmacoscairocontext.c
+++ b/gdk/macos/gdkmacoscairocontext.c
@@ -22,19 +22,17 @@
#include "gdkconfig.h"
+#include
+#include
#include
-#import "GdkMacosCairoView.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 +42,120 @@ 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 const cairo_user_data_key_t buffer_key;
+
+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;
+ GdkMacosBuffer *buffer = data;
- g_assert (GDK_IS_MACOS_SURFACE (surface));
+ g_assert (GDK_IS_MACOS_BUFFER (buffer));
- /* 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;
-}
-
-static cairo_t *
-do_cairo_create (GdkMacosCairoContext *self)
-{
- GdkSurface *surface;
- cairo_t *cr;
-
- 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);
-
- return cr;
+ _gdk_macos_buffer_unlock (buffer);
+ g_clear_object (&buffer);
}
static cairo_t *
_gdk_macos_cairo_context_cairo_create (GdkCairoContext *cairo_context)
{
GdkMacosCairoContext *self = (GdkMacosCairoContext *)cairo_context;
+ const cairo_region_t *damage;
+ cairo_surface_t *image_surface;
+ GdkMacosBuffer *buffer;
+ GdkSurface *surface;
+ NSWindow *nswindow;
+ cairo_t *cr;
+ gpointer data;
+ double scale;
+ guint width;
+ guint height;
+ guint stride;
+ gboolean opaque;
g_assert (GDK_IS_MACOS_CAIRO_CONTEXT (self));
- if (self->cr != NULL)
- return cairo_reference (self->cr);
+ surface = gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (self));
+ nswindow = _gdk_macos_surface_get_native (GDK_MACOS_SURFACE (surface));
+ opaque = [nswindow isOpaque];
- return do_cairo_create (self);
+ buffer = _gdk_macos_surface_get_buffer (GDK_MACOS_SURFACE (surface));
+ damage = _gdk_macos_buffer_get_damage (buffer);
+ width = _gdk_macos_buffer_get_width (buffer);
+ height = _gdk_macos_buffer_get_height (buffer);
+ scale = _gdk_macos_buffer_get_device_scale (buffer);
+ stride = _gdk_macos_buffer_get_stride (buffer);
+ data = _gdk_macos_buffer_get_data (buffer);
+
+ /* Instead of forcing cairo to do everything through a CGContext,
+ * we just use an image surface backed by an IOSurfaceRef mapped
+ * into user-space. We can then use pixman which is quite fast as
+ * far as software rendering goes.
+ *
+ * Additionally, cairo_quartz_surface_t can't handle a number of
+ * tricks that the GSK cairo renderer does with border nodes and
+ * shadows, so an image surface is necessary for that.
+ *
+ * Since our IOSurfaceRef is width*scale-by-height*scale, we undo
+ * the scaling using cairo_surface_set_device_scale() so the renderer
+ * just thinks it's on a 2x scale surface for HiDPI.
+ */
+ image_surface = cairo_image_surface_create_for_data (data,
+ CAIRO_FORMAT_ARGB32,
+ width,
+ height,
+ stride);
+ cairo_surface_set_device_scale (image_surface, scale, scale);
+
+ /* Lock the buffer so we can modify it safely */
+ _gdk_macos_buffer_lock (buffer);
+ cairo_surface_set_user_data (image_surface,
+ &buffer_key,
+ g_object_ref (buffer),
+ unlock_buffer);
+
+ if (!(cr = cairo_create (image_surface)))
+ goto failure;
+
+ /* Clip to the current damage region */
+ if (damage != NULL)
+ {
+ gdk_cairo_region (cr, damage);
+ cairo_clip (cr);
+ }
+
+ /* If we have some exposed transparent area in the damage region,
+ * we need to clear the existing content first to leave an transparent
+ * area for cairo. We use (surface_bounds or damage)-(opaque) to get
+ * the smallest set of rectangles we need to clear as it's expensive.
+ */
+ if (!opaque)
+ {
+ cairo_region_t *transparent;
+ cairo_rectangle_int_t r = { 0, 0, width/scale, height/scale };
+
+ cairo_save (cr);
+
+ if (damage != NULL)
+ cairo_region_get_extents (damage, &r);
+ transparent = cairo_region_create_rectangle (&r);
+ if (surface->opaque_region)
+ cairo_region_subtract (transparent, surface->opaque_region);
+
+ if (!cairo_region_is_empty (transparent))
+ {
+ gdk_cairo_region (cr, transparent);
+ cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
+ cairo_fill (cr);
+ }
+
+ cairo_region_destroy (transparent);
+ cairo_restore (cr);
+ }
+
+failure:
+ cairo_surface_destroy (image_surface);
+
+ return cr;
}
static void
@@ -126,80 +164,53 @@ _gdk_macos_cairo_context_begin_frame (GdkDrawContext *draw_context,
cairo_region_t *region)
{
GdkMacosCairoContext *self = (GdkMacosCairoContext *)draw_context;
+ GdkMacosBuffer *buffer;
GdkSurface *surface;
- NSWindow *nswindow;
g_assert (GDK_IS_MACOS_CAIRO_CONTEXT (self));
+ [CATransaction begin];
+ [CATransaction setDisableActions:YES];
+
surface = gdk_draw_context_get_surface (draw_context);
- nswindow = _gdk_macos_surface_get_native (GDK_MACOS_SURFACE (surface));
+ buffer = _gdk_macos_surface_get_buffer (GDK_MACOS_SURFACE (surface));
- if (self->window_surface == NULL)
- self->window_surface = create_cairo_surface_for_surface (surface);
-
- self->cr = do_cairo_create (self);
-
- 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 (buffer, region);
+ _gdk_macos_buffer_set_flipped (buffer, FALSE);
}
static void
_gdk_macos_cairo_context_end_frame (GdkDrawContext *draw_context,
cairo_region_t *painted)
{
- GdkMacosCairoContext *self = (GdkMacosCairoContext *)draw_context;
+ GdkMacosBuffer *buffer;
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));
+ buffer = _gdk_macos_surface_get_buffer (GDK_MACOS_SURFACE (surface));
- g_clear_pointer (&self->cr, cairo_destroy);
+ _gdk_macos_surface_swap_buffers (GDK_MACOS_SURFACE (surface), painted);
+ _gdk_macos_buffer_set_damage (buffer, NULL);
- if (GDK_IS_MACOS_CAIRO_VIEW (nsview))
- [(GdkMacosCairoView *)nsview setCairoSurface:self->window_surface
- withDamage:painted];
+ [CATransaction commit];
}
static void
_gdk_macos_cairo_context_surface_resized (GdkDrawContext *draw_context)
{
- GdkMacosCairoContext *self = (GdkMacosCairoContext *)draw_context;
+ g_assert (GDK_IS_MACOS_CAIRO_CONTEXT (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);
+ /* Do nothing, next begin_frame will get new buffer */
}
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);
- object_class->dispose = _gdk_macos_cairo_context_dispose;
-
draw_context_class->begin_frame = _gdk_macos_cairo_context_begin_frame;
draw_context_class->end_frame = _gdk_macos_cairo_context_end_frame;
draw_context_class->surface_resized = _gdk_macos_cairo_context_surface_resized;
diff --git a/gdk/macos/gdkmacosdisplay.c b/gdk/macos/gdkmacosdisplay.c
index a8af833eb5..74095504a2 100644
--- a/gdk/macos/gdkmacosdisplay.c
+++ b/gdk/macos/gdkmacosdisplay.c
@@ -173,8 +173,7 @@ gdk_macos_display_monitors_changed_cb (CFNotificationCenterRef center,
_gdk_macos_display_reload_monitors (self);
/* Now we need to update all our surface positions since they
- * probably just changed origins. We ignore the popup surfaces
- * since we can rely on the toplevel surfaces to handle that.
+ * probably just changed origins.
*/
for (const GList *iter = _gdk_macos_display_get_surfaces (self);
iter != NULL;
@@ -184,8 +183,7 @@ gdk_macos_display_monitors_changed_cb (CFNotificationCenterRef center,
g_assert (GDK_IS_MACOS_SURFACE (surface));
- if (GDK_IS_TOPLEVEL (surface))
- _gdk_macos_surface_configure (surface);
+ _gdk_macos_surface_monitor_changed (surface);
}
}
diff --git a/gdk/macos/gdkmacosglcontext-private.h b/gdk/macos/gdkmacosglcontext-private.h
index 781035677a..8b3eac2ca6 100644
--- a/gdk/macos/gdkmacosglcontext-private.h
+++ b/gdk/macos/gdkmacosglcontext-private.h
@@ -29,7 +29,7 @@
#include "gdkmacossurface.h"
#import
-#import
+#import
#import
G_BEGIN_DECLS
@@ -38,17 +38,17 @@ struct _GdkMacosGLContext
{
GdkGLContext parent_instance;
- G_GNUC_BEGIN_IGNORE_DEPRECATIONS
- NSOpenGLContext *gl_context;
- G_GNUC_END_IGNORE_DEPRECATIONS
-
- NSWindow *dummy_window;
- NSView *dummy_view;
-
cairo_region_t *damage;
- guint is_attached : 1;
- guint needs_resize : 1;
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+ CGLContextObj cgl_context;
+ G_GNUC_END_IGNORE_DEPRECATIONS
+
+ GLuint texture;
+ GLuint target;
+ GLuint fbo;
+
+ guint last_opaque : 1;
};
struct _GdkMacosGLContextClass
@@ -56,7 +56,6 @@ struct _GdkMacosGLContextClass
GdkGLContextClass parent_class;
};
-
G_END_DECLS
#endif /* __GDK_MACOS_GL_CONTEXT_PRIVATE_H__ */
diff --git a/gdk/macos/gdkmacosglcontext.c b/gdk/macos/gdkmacosglcontext.c
index 1800815786..5baff95a9b 100644
--- a/gdk/macos/gdkmacosglcontext.c
+++ b/gdk/macos/gdkmacosglcontext.c
@@ -19,20 +19,139 @@
#include "config.h"
+#include "gdkconfig.h"
+
+#include
+#include
+#include
+
+#include "gdkmacosbuffer-private.h"
#include "gdkmacosglcontext-private.h"
#include "gdkmacossurface-private.h"
-#include "gdkmacostoplevelsurface-private.h"
-
-#include "gdkintl.h"
-
-#include
-
-#import "GdkMacosGLView.h"
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
G_DEFINE_TYPE (GdkMacosGLContext, gdk_macos_gl_context, GDK_TYPE_GL_CONTEXT)
+#define CHECK(error,cgl_error) _CHECK_CGL(error, G_STRLOC, cgl_error)
+static inline gboolean
+_CHECK_CGL (GError **error,
+ const char *location,
+ CGLError cgl_error)
+{
+ if (cgl_error != kCGLNoError)
+ {
+ g_log ("Core OpenGL",
+ G_LOG_LEVEL_CRITICAL,
+ "%s: %s",
+ location, CGLErrorString (cgl_error));
+ g_set_error (error,
+ GDK_GL_ERROR,
+ GDK_GL_ERROR_NOT_AVAILABLE,
+ "%s",
+ CGLErrorString (cgl_error));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Apple's OpenGL implementation does not contain the extension to
+ * perform log handler callbacks when errors occur. Therefore, to aid in
+ * tracking down issues we have a CHECK_GL() macro that can wrap GL
+ * calls and check for an error afterwards.
+ *
+ * To make this easier, we use a statement expression, as this will
+ * always be using something GCC-compatible on macOS.
+ */
+#define CHECK_GL(error,func) _CHECK_GL(error, G_STRLOC, ({ func; glGetError(); }))
+static inline gboolean
+_CHECK_GL (GError **error,
+ const char *location,
+ GLenum gl_error)
+{
+ const char *msg;
+
+ switch (gl_error)
+ {
+ case GL_INVALID_ENUM:
+ msg = "invalid enum";
+ break;
+ case GL_INVALID_VALUE:
+ msg = "invalid value";
+ break;
+ case GL_INVALID_OPERATION:
+ msg = "invalid operation";
+ break;
+ case GL_INVALID_FRAMEBUFFER_OPERATION:
+ msg = "invalid framebuffer operation";
+ break;
+ case GL_OUT_OF_MEMORY:
+ msg = "out of memory";
+ break;
+ default:
+ msg = "unknown error";
+ break;
+ }
+
+ if (gl_error != GL_NO_ERROR)
+ {
+ g_log ("OpenGL",
+ G_LOG_LEVEL_CRITICAL,
+ "%s: %s", location, msg);
+ if (error != NULL)
+ g_set_error (error,
+ GDK_GL_ERROR,
+ GDK_GL_ERROR_NOT_AVAILABLE,
+ "%s", msg);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+check_framebuffer_status (GLenum target)
+{
+ switch (glCheckFramebufferStatus (target))
+ {
+ case GL_FRAMEBUFFER_COMPLETE:
+ return TRUE;
+
+ case GL_FRAMEBUFFER_UNDEFINED:
+ g_critical ("Framebuffer is undefined");
+ return FALSE;
+
+ case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
+ g_critical ("Framebuffer has incomplete attachment");
+ return FALSE;
+
+ case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
+ g_critical ("Framebuffer has missing attachment");
+ return FALSE;
+
+ case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
+ g_critical ("Framebuffer has incomplete draw buffer");
+ return FALSE;
+
+ case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
+ g_critical ("Framebuffer has incomplete read buffer");
+ return FALSE;
+
+ case GL_FRAMEBUFFER_UNSUPPORTED:
+ g_critical ("Framebuffer is unsupported");
+ return FALSE;
+
+ case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
+ g_critical ("Framebuffer has incomplete multisample");
+ return FALSE;
+
+ default:
+ g_critical ("Framebuffer has unknown error");
+ return FALSE;
+ }
+}
+
static const char *
get_renderer_name (GLint id)
{
@@ -72,97 +191,165 @@ get_renderer_name (GLint id)
}
}
-static NSOpenGLContext *
-get_ns_open_gl_context (GdkMacosGLContext *self,
- GError **error)
+static GLuint
+create_texture (CGLContextObj cgl_context,
+ GLuint target,
+ IOSurfaceRef io_surface,
+ guint width,
+ guint height)
{
- g_assert (GDK_IS_MACOS_GL_CONTEXT (self));
+ GLuint texture = 0;
- if (self->gl_context == nil)
+ if (!CHECK_GL (NULL, glActiveTexture (GL_TEXTURE0)) ||
+ !CHECK_GL (NULL, glGenTextures (1, &texture)) ||
+ !CHECK_GL (NULL, glBindTexture (target, texture)) ||
+ !CHECK (NULL, CGLTexImageIOSurface2D (cgl_context,
+ target,
+ GL_RGBA,
+ width,
+ height,
+ GL_BGRA,
+ GL_UNSIGNED_INT_8_8_8_8_REV,
+ io_surface,
+ 0)) ||
+ !CHECK_GL (NULL, glTexParameteri (target, GL_TEXTURE_BASE_LEVEL, 0)) ||
+ !CHECK_GL (NULL, glTexParameteri (target, GL_TEXTURE_MIN_FILTER, GL_NEAREST)) ||
+ !CHECK_GL (NULL, glTexParameteri (target, GL_TEXTURE_MAG_FILTER, GL_NEAREST)) ||
+ !CHECK_GL (NULL, glTexParameteri (target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)) ||
+ !CHECK_GL (NULL, glTexParameteri (target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE)) ||
+ !CHECK_GL (NULL, glTexParameteri (target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)) ||
+ !CHECK_GL (NULL, glBindTexture (target, 0)))
{
- g_set_error_literal (error,
- GDK_GL_ERROR,
- GDK_GL_ERROR_NOT_AVAILABLE,
- "Cannot access NSOpenGLContext for surface");
- return NULL;
+ glDeleteTextures (1, &texture);
+ return 0;
}
- return self->gl_context;
+ return texture;
}
-static NSOpenGLPixelFormat *
+static void
+gdk_macos_gl_context_allocate (GdkMacosGLContext *self)
+{
+ GdkSurface *surface;
+ GLint opaque;
+
+ g_assert (GDK_IS_MACOS_GL_CONTEXT (self));
+ g_assert (self->cgl_context != NULL);
+ g_assert (self->target != 0);
+ g_assert (self->texture != 0 || self->fbo == 0);
+ g_assert (self->fbo != 0 || self->texture == 0);
+
+ if (!(surface = gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (self))))
+ return;
+
+ /* Alter to an opaque surface if necessary */
+ opaque = _gdk_macos_surface_is_opaque (GDK_MACOS_SURFACE (surface));
+ if (opaque != self->last_opaque)
+ {
+ self->last_opaque = !!opaque;
+ if (!CHECK (NULL, CGLSetParameter (self->cgl_context, kCGLCPSurfaceOpacity, &opaque)))
+ return;
+ }
+
+ if (self->texture == 0)
+ {
+ GdkMacosBuffer *buffer;
+ IOSurfaceRef io_surface;
+ guint width;
+ guint height;
+ GLuint texture = 0;
+ GLuint fbo = 0;
+
+ buffer = _gdk_macos_surface_get_buffer (GDK_MACOS_SURFACE (surface));
+ io_surface = _gdk_macos_buffer_get_native (buffer);
+ width = _gdk_macos_buffer_get_width (buffer);
+ height = _gdk_macos_buffer_get_height (buffer);
+
+ /* We might need to re-enforce our CGL context here to keep
+ * video playing correctly. Something, somewhere, might have
+ * changed the context without touching GdkGLContext.
+ *
+ * Without this, video_player often breaks in gtk-demo when using
+ * the GStreamer backend.
+ */
+ CGLSetCurrentContext (self->cgl_context);
+
+ if (!(texture = create_texture (self->cgl_context, self->target, io_surface, width, height)) ||
+ !CHECK_GL (NULL, glGenFramebuffers (1, &fbo)) ||
+ !CHECK_GL (NULL, glBindFramebuffer (GL_FRAMEBUFFER, fbo)) ||
+ !CHECK_GL (NULL, glBindTexture (self->target, texture)) ||
+ !CHECK_GL (NULL, glFramebufferTexture2D (GL_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ self->target,
+ texture,
+ 0)) ||
+ !check_framebuffer_status (GL_FRAMEBUFFER))
+ {
+ glDeleteFramebuffers (1, &fbo);
+ glDeleteTextures (1, &texture);
+ return;
+ }
+
+ glBindTexture (self->target, 0);
+ glBindFramebuffer (GL_FRAMEBUFFER, 0);
+
+ self->texture = texture;
+ self->fbo = fbo;
+ }
+}
+
+static void
+gdk_macos_gl_context_release (GdkMacosGLContext *self)
+{
+ g_assert (GDK_IS_MACOS_GL_CONTEXT (self));
+ g_assert (self->texture != 0 || self->fbo == 0);
+ g_assert (self->fbo != 0 || self->texture == 0);
+
+ glBindFramebuffer (GL_FRAMEBUFFER, 0);
+ glActiveTexture (GL_TEXTURE0);
+ glBindTexture (self->target, 0);
+
+ if (self->fbo != 0)
+ {
+ glDeleteFramebuffers (1, &self->fbo);
+ self->fbo = 0;
+ }
+
+ if (self->texture != 0)
+ {
+ glDeleteTextures (1, &self->texture);
+ self->texture = 0;
+ }
+}
+
+static CGLPixelFormatObj
create_pixel_format (int major,
int minor,
GError **error)
{
- NSOpenGLPixelFormatAttribute attrs[] = {
- NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersionLegacy,
- NSOpenGLPFAAccelerated,
- NSOpenGLPFADoubleBuffer,
- NSOpenGLPFABackingStore,
- NSOpenGLPFAColorSize, 24,
- NSOpenGLPFAAlphaSize, 8,
+ CGLPixelFormatAttribute attrs[] = {
+ kCGLPFAOpenGLProfile, (CGLPixelFormatAttribute)kCGLOGLPVersion_Legacy,
+ kCGLPFAAllowOfflineRenderers, /* allow sharing across GPUs */
+ kCGLPFADepthSize, 0,
+ kCGLPFAStencilSize, 0,
+ kCGLPFAColorSize, 24,
+ kCGLPFAAlphaSize, 8,
0
};
+ CGLPixelFormatObj format = NULL;
+ GLint n_format = 1;
if (major == 3 && minor == 2)
- attrs[1] = NSOpenGLProfileVersion3_2Core;
+ attrs[1] = (CGLPixelFormatAttribute)kCGLOGLPVersion_GL3_Core;
else if (major == 4 && minor == 1)
- attrs[1] = NSOpenGLProfileVersion4_1Core;
+ attrs[1] = (CGLPixelFormatAttribute)kCGLOGLPVersion_GL4_Core;
- NSOpenGLPixelFormat *format = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
-
- if (format == NULL)
- g_set_error (error,
- GDK_GL_ERROR,
- GDK_GL_ERROR_NOT_AVAILABLE,
- "Failed to create pixel format");
+ if (!CHECK (error, CGLChoosePixelFormat (attrs, &format, &n_format)))
+ return NULL;
return g_steal_pointer (&format);
}
-static NSView *
-ensure_gl_view (GdkMacosGLContext *self)
-{
- GdkMacosSurface *surface;
- NSWindow *nswindow;
- NSView *nsview;
-
- g_assert (GDK_IS_MACOS_GL_CONTEXT (self));
-
- surface = GDK_MACOS_SURFACE (gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (self)));
- nsview = _gdk_macos_surface_get_view (surface);
- nswindow = _gdk_macos_surface_get_native (surface);
-
- if G_UNLIKELY (!GDK_IS_MACOS_GL_VIEW (nsview))
- {
- NSRect frame;
-
- frame = [[nswindow contentView] bounds];
- nsview = [[GdkMacosGLView alloc] initWithFrame:frame];
- [nsview setWantsBestResolutionOpenGLSurface:YES];
- [nsview setPostsFrameChangedNotifications: YES];
- [nsview setNeedsDisplay:YES];
- [nswindow setContentView:nsview];
- [nswindow makeFirstResponder:nsview];
- [nsview release];
-
- if (self->dummy_view != NULL)
- {
- NSView *dummy_view = g_steal_pointer (&self->dummy_view);
- [dummy_view release];
- }
-
- if (self->dummy_window != NULL)
- {
- NSWindow *dummy_window = g_steal_pointer (&self->dummy_window);
- [dummy_window release];
- }
- }
-
- return [nswindow contentView];
-}
-
static GdkGLAPI
gdk_macos_gl_context_real_realize (GdkGLContext *context,
GError **error)
@@ -170,195 +357,130 @@ gdk_macos_gl_context_real_realize (GdkGLContext *context,
GdkMacosGLContext *self = (GdkMacosGLContext *)context;
GdkSurface *surface;
GdkDisplay *display;
- NSOpenGLContext *shared_gl_context = nil;
- NSOpenGLContext *gl_context;
- NSOpenGLPixelFormat *pixelFormat;
+ CGLPixelFormatObj pixelFormat;
+ CGLContextObj shared_gl_context = nil;
CGLContextObj cgl_context;
+ CGLContextObj existing;
GdkGLContext *shared;
- NSOpenGLContext *existing;
GLint sync_to_framerate = 1;
GLint validate = 0;
+ GLint renderer_id = 0;
GLint swapRect[4];
int major, minor;
g_assert (GDK_IS_MACOS_GL_CONTEXT (self));
- if (self->gl_context != nil)
+ if (self->cgl_context != nil)
return GDK_GL_API_GL;
if (!gdk_gl_context_is_api_allowed (context, GDK_GL_API_GL, error))
return 0;
- existing = [NSOpenGLContext currentContext];
+ existing = CGLGetCurrentContext ();
gdk_gl_context_get_required_version (context, &major, &minor);
- surface = gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (context));
display = gdk_gl_context_get_display (context);
shared = gdk_display_get_gl_context (display);
if (shared != NULL)
{
- if (!(shared_gl_context = get_ns_open_gl_context (GDK_MACOS_GL_CONTEXT (shared), error)))
- return 0;
+ if (!(shared_gl_context = GDK_MACOS_GL_CONTEXT (shared)->cgl_context))
+ {
+ g_set_error_literal (error,
+ GDK_GL_ERROR,
+ GDK_GL_ERROR_NOT_AVAILABLE,
+ "Cannot access shared CGLContextObj");
+ return 0;
+ }
}
GDK_DISPLAY_NOTE (display,
OPENGL,
- g_message ("Creating NSOpenGLContext (version %d.%d)",
+ g_message ("Creating CGLContextObj (version %d.%d)",
major, minor));
if (!(pixelFormat = create_pixel_format (major, minor, error)))
return 0;
- gl_context = [[NSOpenGLContext alloc] initWithFormat:pixelFormat
- shareContext:shared_gl_context];
-
- [pixelFormat release];
-
- if (gl_context == nil)
+ if (!CHECK (error, CGLCreateContext (pixelFormat, shared_gl_context, &cgl_context)))
{
- g_set_error_literal (error,
- GDK_GL_ERROR,
- GDK_GL_ERROR_NOT_AVAILABLE,
- "Failed to create NSOpenGLContext");
+ CGLReleasePixelFormat (pixelFormat);
return 0;
}
- cgl_context = [gl_context CGLContextObj];
+ CGLSetCurrentContext (cgl_context);
+ CGLReleasePixelFormat (pixelFormat);
- swapRect[0] = 0;
- swapRect[1] = 0;
- swapRect[2] = surface ? surface->width : 0;
- swapRect[3] = surface ? surface->height : 0;
-
- CGLSetParameter (cgl_context, kCGLCPSwapRectangle, swapRect);
- CGLSetParameter (cgl_context, kCGLCPSwapInterval, &sync_to_framerate);
-
- CGLEnable (cgl_context, kCGLCESwapRectangle);
if (validate)
- CGLEnable (cgl_context, kCGLCEStateValidation);
+ CHECK (NULL, CGLEnable (cgl_context, kCGLCEStateValidation));
- self->dummy_window = [[NSWindow alloc] initWithContentRect:NSZeroRect
- styleMask:0
- backing:NSBackingStoreBuffered
- defer:NO
- screen:nil];
- self->dummy_view = [[NSView alloc] initWithFrame:NSZeroRect];
- [self->dummy_window setContentView:self->dummy_view];
- [gl_context setView:self->dummy_view];
+ if (!CHECK (error, CGLSetParameter (cgl_context, kCGLCPSwapInterval, &sync_to_framerate)) ||
+ !CHECK (error, CGLGetParameter (cgl_context, kCGLCPCurrentRendererID, &renderer_id)))
+ {
+ CGLReleaseContext (cgl_context);
+ return 0;
+ }
- GLint renderer_id = 0;
- [gl_context getValues:&renderer_id forParameter:NSOpenGLContextParameterCurrentRendererID];
- GDK_DISPLAY_NOTE (display,
- OPENGL,
- g_message ("Created NSOpenGLContext[%p] using %s",
- gl_context,
- get_renderer_name (renderer_id)));
+ surface = gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (context));
- self->gl_context = g_steal_pointer (&gl_context);
-
- if (existing != NULL)
- [existing makeCurrentContext];
-
- return GDK_GL_API_GL;
-}
-
-static gboolean
-opaque_region_covers_surface (GdkMacosGLContext *self)
-{
- GdkSurface *surface;
- cairo_region_t *region;
-
- g_assert (GDK_IS_MACOS_GL_CONTEXT (self));
-
- surface = gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (self));
- region = GDK_MACOS_SURFACE (surface)->opaque_region;
-
- if (region != NULL &&
- cairo_region_num_rectangles (region) == 1)
+ if (surface != NULL)
{
- cairo_rectangle_int_t extents;
-
- cairo_region_get_extents (region, &extents);
-
- if (extents.x == 0 &&
- extents.y == 0 &&
- extents.width == surface->width &&
- extents.height == surface->height)
- return TRUE;
+ /* Setup initial swap rectangle. We might not actually need this
+ * anymore though as we are rendering to an IOSurface and we have
+ * a scissor clip when rendering to it.
+ */
+ swapRect[0] = 0;
+ swapRect[1] = 0;
+ swapRect[2] = surface->width;
+ swapRect[3] = surface->height;
+ CGLSetParameter (cgl_context, kCGLCPSwapRectangle, swapRect);
+ CGLEnable (cgl_context, kCGLCESwapRectangle);
}
- return FALSE;
+ GDK_DISPLAY_NOTE (display,
+ OPENGL,
+ g_message ("Created CGLContextObj@%p using %s",
+ cgl_context,
+ get_renderer_name (renderer_id)));
+
+ self->cgl_context = g_steal_pointer (&cgl_context);
+
+ if (existing != NULL)
+ CGLSetCurrentContext (existing);
+
+ return GDK_GL_API_GL;
}
static void
gdk_macos_gl_context_begin_frame (GdkDrawContext *context,
gboolean prefers_high_depth,
- cairo_region_t *painted)
+ cairo_region_t *region)
{
GdkMacosGLContext *self = (GdkMacosGLContext *)context;
+ GdkMacosBuffer *buffer;
+ cairo_region_t *copy;
GdkSurface *surface;
g_assert (GDK_IS_MACOS_GL_CONTEXT (self));
+ copy = cairo_region_copy (region);
surface = gdk_draw_context_get_surface (context);
+ buffer = _gdk_macos_surface_get_buffer (GDK_MACOS_SURFACE (surface));
+
+ _gdk_macos_buffer_set_flipped (buffer, TRUE);
+
+ /* Create our render target and bind it */
+ gdk_gl_context_make_current (GDK_GL_CONTEXT (self));
+ gdk_macos_gl_context_allocate (self);
+
+ GDK_DRAW_CONTEXT_CLASS (gdk_macos_gl_context_parent_class)->begin_frame (context, prefers_high_depth, region);
g_clear_pointer (&self->damage, cairo_region_destroy);
- self->damage = cairo_region_copy (painted);
+ self->damage = g_steal_pointer (©);
- /* If begin frame is called, that means we are trying to draw to
- * the NSWindow using our view. That might be a GdkMacosCairoView
- * but we need it to be a GL view. Also, only in this case do we
- * want to replace our damage region for the next frame (to avoid
- * doing it multiple times).
- */
- ensure_gl_view (self);
-
- if (self->needs_resize)
- {
- CGLContextObj cgl_context = [self->gl_context CGLContextObj];
- GLint opaque;
-
- self->needs_resize = FALSE;
-
- if (self->dummy_view != NULL)
- {
- NSRect frame = NSMakeRect (0, 0, surface->width, surface->height);
-
- [self->dummy_window setFrame:frame display:NO];
- [self->dummy_view setFrame:frame];
- }
-
- /* Possibly update our opaque setting depending on a resize. We can
- * rely on getting a resize if decoarated is changed, so this reduces
- * how much we adjust the parameter.
- */
- if (GDK_IS_MACOS_TOPLEVEL_SURFACE (surface))
- opaque = GDK_MACOS_TOPLEVEL_SURFACE (surface)->decorated;
- else
- opaque = FALSE;
-
- /* If we are maximized, we might be able to make it opaque */
- if (opaque == FALSE)
- opaque = opaque_region_covers_surface (self);
-
- CGLSetParameter (cgl_context, kCGLCPSurfaceOpacity, &opaque);
-
- [self->gl_context update];
- }
-
- GDK_DRAW_CONTEXT_CLASS (gdk_macos_gl_context_parent_class)->begin_frame (context, prefers_high_depth, painted);
-
- if (!self->is_attached)
- {
- NSView *nsview = _gdk_macos_surface_get_view (GDK_MACOS_SURFACE (surface));
-
- g_assert (self->gl_context != NULL);
- g_assert (GDK_IS_MACOS_GL_VIEW (nsview));
-
- [(GdkMacosGLView *)nsview setOpenGLContext:self->gl_context];
- }
+ gdk_gl_context_make_current (GDK_GL_CONTEXT (self));
+ CHECK_GL (NULL, glBindFramebuffer (GL_FRAMEBUFFER, self->fbo));
}
static void
@@ -366,32 +488,40 @@ gdk_macos_gl_context_end_frame (GdkDrawContext *context,
cairo_region_t *painted)
{
GdkMacosGLContext *self = GDK_MACOS_GL_CONTEXT (context);
+ GdkSurface *surface;
+ cairo_rectangle_int_t flush_rect;
+ GLint swapRect[4];
g_assert (GDK_IS_MACOS_GL_CONTEXT (self));
- g_assert (self->gl_context != nil);
+ g_assert (self->cgl_context != nil);
GDK_DRAW_CONTEXT_CLASS (gdk_macos_gl_context_parent_class)->end_frame (context, painted);
- if (!self->is_attached)
- {
- GdkSurface *surface = gdk_draw_context_get_surface (context);
- CGLContextObj glctx = [self->gl_context CGLContextObj];
- cairo_rectangle_int_t flush_rect;
- GLint swapRect[4];
+ surface = gdk_draw_context_get_surface (context);
+ gdk_gl_context_make_current (GDK_GL_CONTEXT (self));
- /* Coordinates are in display coordinates, where as flush_rect is
- * in GDK coordinates. Must flip Y to match display coordinates where
- * 0,0 is the bottom-left corner.
- */
- cairo_region_get_extents (painted, &flush_rect);
- swapRect[0] = flush_rect.x; /* left */
- swapRect[1] = surface->height - flush_rect.y; /* bottom */
- swapRect[2] = flush_rect.width; /* width */
- swapRect[3] = flush_rect.height; /* height */
- CGLSetParameter (glctx, kCGLCPSwapRectangle, swapRect);
+ /* Coordinates are in display coordinates, where as flush_rect is
+ * in GDK coordinates. Must flip Y to match display coordinates where
+ * 0,0 is the bottom-left corner.
+ */
+ cairo_region_get_extents (painted, &flush_rect);
+ swapRect[0] = flush_rect.x; /* left */
+ swapRect[1] = surface->height - flush_rect.y; /* bottom */
+ swapRect[2] = flush_rect.width; /* width */
+ swapRect[3] = flush_rect.height; /* height */
+ CGLSetParameter (self->cgl_context, kCGLCPSwapRectangle, swapRect);
- [self->gl_context flushBuffer];
- }
+ gdk_macos_gl_context_release (self);
+
+ glFlush ();
+
+ /* Begin a Core Animation transaction so that all changes we
+ * make within the window are seen atomically.
+ */
+ [CATransaction begin];
+ [CATransaction setDisableActions:YES];
+ _gdk_macos_surface_swap_buffers (GDK_MACOS_SURFACE (surface), painted);
+ [CATransaction commit];
}
static void
@@ -401,31 +531,23 @@ gdk_macos_gl_context_surface_resized (GdkDrawContext *draw_context)
g_assert (GDK_IS_MACOS_GL_CONTEXT (self));
- self->needs_resize = TRUE;
-
g_clear_pointer (&self->damage, cairo_region_destroy);
+
+ if (self->cgl_context != NULL)
+ CGLUpdateContext (self->cgl_context);
}
static gboolean
gdk_macos_gl_context_clear_current (GdkGLContext *context)
{
GdkMacosGLContext *self = GDK_MACOS_GL_CONTEXT (context);
- NSOpenGLContext *current;
g_return_val_if_fail (GDK_IS_MACOS_GL_CONTEXT (self), FALSE);
- current = [NSOpenGLContext currentContext];
-
- if (self->gl_context == current)
+ if (self->cgl_context == CGLGetCurrentContext ())
{
- /* The OpenGL mac programming guide suggests that glFlush() is called
- * before switching current contexts to ensure that the drawing commands
- * are submitted.
- */
- if (current != NULL)
- glFlush ();
-
- [NSOpenGLContext clearCurrentContext];
+ glFlush ();
+ CGLSetCurrentContext (NULL);
}
return TRUE;
@@ -436,22 +558,26 @@ gdk_macos_gl_context_make_current (GdkGLContext *context,
gboolean surfaceless)
{
GdkMacosGLContext *self = GDK_MACOS_GL_CONTEXT (context);
- NSOpenGLContext *current;
+ CGLContextObj current;
g_return_val_if_fail (GDK_IS_MACOS_GL_CONTEXT (self), FALSE);
- current = [NSOpenGLContext currentContext];
+ current = CGLGetCurrentContext ();
- if (self->gl_context != current)
+ if (self->cgl_context != current)
{
/* The OpenGL mac programming guide suggests that glFlush() is called
* before switching current contexts to ensure that the drawing commands
* are submitted.
+ *
+ * TODO: investigate if we need this because we may switch contexts
+ * durring composition and only need it when returning to a
+ * previous context that uses the other context.
*/
if (current != NULL)
glFlush ();
- [self->gl_context makeCurrentContext];
+ CGLSetCurrentContext (self->cgl_context);
}
return TRUE;
@@ -462,40 +588,35 @@ gdk_macos_gl_context_get_damage (GdkGLContext *context)
{
GdkMacosGLContext *self = (GdkMacosGLContext *)context;
- g_assert (GDK_IS_MACOS_GL_CONTEXT (self));
-
- if (self->damage != NULL)
+ if (self->damage)
return cairo_region_copy (self->damage);
return GDK_GL_CONTEXT_CLASS (gdk_macos_gl_context_parent_class)->get_damage (context);
}
+static guint
+gdk_macos_gl_context_get_default_framebuffer (GdkGLContext *context)
+{
+ return GDK_MACOS_GL_CONTEXT (context)->fbo;
+}
+
static void
gdk_macos_gl_context_dispose (GObject *gobject)
{
GdkMacosGLContext *self = GDK_MACOS_GL_CONTEXT (gobject);
- if (self->dummy_view != nil)
+ self->texture = 0;
+ self->fbo = 0;
+
+ if (self->cgl_context != nil)
{
- NSView *nsview = g_steal_pointer (&self->dummy_view);
- [nsview release];
- }
+ CGLContextObj cgl_context = g_steal_pointer (&self->cgl_context);
- if (self->dummy_window != nil)
- {
- NSWindow *nswindow = g_steal_pointer (&self->dummy_window);
- [nswindow release];
- }
+ if (cgl_context == CGLGetCurrentContext ())
+ CGLSetCurrentContext (NULL);
- if (self->gl_context != nil)
- {
- NSOpenGLContext *gl_context = g_steal_pointer (&self->gl_context);
-
- if (gl_context == [NSOpenGLContext currentContext])
- [NSOpenGLContext clearCurrentContext];
-
- [gl_context clearDrawable];
- [gl_context release];
+ CGLClearDrawable (cgl_context);
+ CGLDestroyContext (cgl_context);
}
g_clear_pointer (&self->damage, cairo_region_destroy);
@@ -520,6 +641,7 @@ gdk_macos_gl_context_class_init (GdkMacosGLContextClass *klass)
gl_class->clear_current = gdk_macos_gl_context_clear_current;
gl_class->make_current = gdk_macos_gl_context_make_current;
gl_class->realize = gdk_macos_gl_context_real_realize;
+ gl_class->get_default_framebuffer = gdk_macos_gl_context_get_default_framebuffer;
gl_class->backend_type = GDK_GL_CGL;
}
@@ -527,6 +649,7 @@ gdk_macos_gl_context_class_init (GdkMacosGLContextClass *klass)
static void
gdk_macos_gl_context_init (GdkMacosGLContext *self)
{
+ self->target = GL_TEXTURE_RECTANGLE;
}
G_GNUC_END_IGNORE_DEPRECATIONS
diff --git a/gdk/macos/gdkmacossurface-private.h b/gdk/macos/gdkmacossurface-private.h
index c8485f88dd..2abc199698 100644
--- a/gdk/macos/gdkmacossurface-private.h
+++ b/gdk/macos/gdkmacossurface-private.h
@@ -25,6 +25,7 @@
#include "gdksurfaceprivate.h"
+#include "gdkmacosbuffer-private.h"
#include "gdkmacosdisplay.h"
#include "gdkmacossurface.h"
@@ -45,8 +46,9 @@ struct _GdkMacosSurface
GList frame;
GdkMacosWindow *window;
+ GdkMacosBuffer *buffer;
+ GdkMacosBuffer *front;
GPtrArray *monitors;
- cairo_region_t *input_region;
cairo_region_t *opaque_region;
char *title;
@@ -65,10 +67,14 @@ struct _GdkMacosSurface
int shadow_bottom;
int shadow_left;
+ cairo_rectangle_int_t next_frame;
+
gint64 pending_frame_counter;
guint did_initial_present : 1;
guint geometry_dirty : 1;
+ guint next_frame_set : 1;
+ guint show_on_next_swap : 1;
};
struct _GdkMacosSurfaceClass
@@ -76,71 +82,70 @@ struct _GdkMacosSurfaceClass
GdkSurfaceClass parent_class;
};
-GdkMacosSurface *_gdk_macos_surface_new (GdkMacosDisplay *display,
- GdkSurfaceType surface_type,
- GdkSurface *parent,
- int x,
- int y,
- int width,
- int height);
-NSWindow *_gdk_macos_surface_get_native (GdkMacosSurface *self);
-CGDirectDisplayID _gdk_macos_surface_get_screen_id (GdkMacosSurface *self);
-const char *_gdk_macos_surface_get_title (GdkMacosSurface *self);
-void _gdk_macos_surface_set_title (GdkMacosSurface *self,
- const char *title);
-void _gdk_macos_surface_get_shadow (GdkMacosSurface *self,
- int *top,
- int *right,
- int *bottom,
- int *left);
-void _gdk_macos_surface_set_shadow (GdkMacosSurface *self,
- int top,
- int right,
- int bottom,
- int left);
-NSView *_gdk_macos_surface_get_view (GdkMacosSurface *self);
-gboolean _gdk_macos_surface_get_modal_hint (GdkMacosSurface *self);
-void _gdk_macos_surface_set_modal_hint (GdkMacosSurface *self,
- gboolean modal_hint);
-void _gdk_macos_surface_set_geometry_hints (GdkMacosSurface *self,
- const GdkGeometry *geometry,
- GdkSurfaceHints geom_mask);
-void _gdk_macos_surface_resize (GdkMacosSurface *self,
- int width,
- int height);
-void _gdk_macos_surface_update_fullscreen_state (GdkMacosSurface *self);
-void _gdk_macos_surface_show (GdkMacosSurface *self);
-void _gdk_macos_surface_publish_timings (GdkMacosSurface *self,
- gint64 predicted_presentation_time,
- gint64 refresh_interval);
-CGContextRef _gdk_macos_surface_acquire_context (GdkMacosSurface *self,
- gboolean clear_scale,
- gboolean antialias);
-void _gdk_macos_surface_release_context (GdkMacosSurface *self,
- CGContextRef cg_context);
-void _gdk_macos_surface_synthesize_null_key (GdkMacosSurface *self);
-void _gdk_macos_surface_move (GdkMacosSurface *self,
- int x,
- int y);
-void _gdk_macos_surface_move_resize (GdkMacosSurface *self,
- int x,
- int y,
- int width,
- int height);
+GdkMacosSurface *_gdk_macos_surface_new (GdkMacosDisplay *display,
+ GdkSurfaceType surface_type,
+ GdkSurface *parent,
+ int x,
+ int y,
+ int width,
+ int height);
+NSWindow *_gdk_macos_surface_get_native (GdkMacosSurface *self);
+CGDirectDisplayID _gdk_macos_surface_get_screen_id (GdkMacosSurface *self);
+const char *_gdk_macos_surface_get_title (GdkMacosSurface *self);
+void _gdk_macos_surface_set_title (GdkMacosSurface *self,
+ const char *title);
+void _gdk_macos_surface_get_shadow (GdkMacosSurface *self,
+ int *top,
+ int *right,
+ int *bottom,
+ int *left);
+void _gdk_macos_surface_set_shadow (GdkMacosSurface *self,
+ int top,
+ int right,
+ int bottom,
+ int left);
+gboolean _gdk_macos_surface_is_opaque (GdkMacosSurface *self);
+NSView *_gdk_macos_surface_get_view (GdkMacosSurface *self);
+gboolean _gdk_macos_surface_get_modal_hint (GdkMacosSurface *self);
+void _gdk_macos_surface_set_modal_hint (GdkMacosSurface *self,
+ gboolean modal_hint);
+void _gdk_macos_surface_set_geometry_hints (GdkMacosSurface *self,
+ const GdkGeometry *geometry,
+ GdkSurfaceHints geom_mask);
+void _gdk_macos_surface_resize (GdkMacosSurface *self,
+ int width,
+ int height);
+void _gdk_macos_surface_update_fullscreen_state (GdkMacosSurface *self);
+void _gdk_macos_surface_update_position (GdkMacosSurface *self);
+void _gdk_macos_surface_show (GdkMacosSurface *self);
+void _gdk_macos_surface_publish_timings (GdkMacosSurface *self,
+ gint64 predicted_presentation_time,
+ gint64 refresh_interval);
+void _gdk_macos_surface_synthesize_null_key (GdkMacosSurface *self);
+void _gdk_macos_surface_move (GdkMacosSurface *self,
+ int x,
+ int y);
+void _gdk_macos_surface_move_resize (GdkMacosSurface *self,
+ int x,
+ int y,
+ int width,
+ int height);
void _gdk_macos_surface_configure (GdkMacosSurface *self);
void _gdk_macos_surface_user_resize (GdkMacosSurface *self,
CGRect new_frame);
-gboolean _gdk_macos_surface_is_tracking (GdkMacosSurface *self,
- NSTrackingArea *area);
-void _gdk_macos_surface_monitor_changed (GdkMacosSurface *self);
-GdkMonitor *_gdk_macos_surface_get_best_monitor (GdkMacosSurface *self);
-void _gdk_macos_surface_reposition_children (GdkMacosSurface *self);
-void _gdk_macos_surface_set_opacity (GdkMacosSurface *self,
- double opacity);
-void _gdk_macos_surface_get_root_coords (GdkMacosSurface *self,
- int *x,
- int *y);
-gboolean _gdk_macos_surface_is_opaque (GdkMacosSurface *self);
+gboolean _gdk_macos_surface_is_tracking (GdkMacosSurface *self,
+ NSTrackingArea *area);
+void _gdk_macos_surface_monitor_changed (GdkMacosSurface *self);
+GdkMonitor *_gdk_macos_surface_get_best_monitor (GdkMacosSurface *self);
+void _gdk_macos_surface_reposition_children (GdkMacosSurface *self);
+void _gdk_macos_surface_set_opacity (GdkMacosSurface *self,
+ double opacity);
+void _gdk_macos_surface_get_root_coords (GdkMacosSurface *self,
+ int *x,
+ int *y);
+GdkMacosBuffer *_gdk_macos_surface_get_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 ded5c19189..b49f4bb333 100644
--- a/gdk/macos/gdkmacossurface.c
+++ b/gdk/macos/gdkmacossurface.c
@@ -23,7 +23,7 @@
#include
#include
-#import "GdkMacosCairoView.h"
+#import "GdkMacosView.h"
#include "gdkmacossurface-private.h"
@@ -63,6 +63,7 @@ window_is_fullscreen (GdkMacosSurface *self)
return ([self->window styleMask] & NSWindowStyleMaskFullScreen) != 0;
}
+
void
_gdk_macos_surface_reposition_children (GdkMacosSurface *self)
{
@@ -122,9 +123,8 @@ gdk_macos_surface_set_opaque_region (GdkSurface *surface,
self->opaque_region = cairo_region_copy (region);
}
- if ((nsview = _gdk_macos_surface_get_view (GDK_MACOS_SURFACE (surface))) &&
- GDK_IS_MACOS_CAIRO_VIEW (nsview))
- [(GdkMacosCairoView *)nsview setOpaqueRegion:region];
+ if ((nsview = _gdk_macos_surface_get_view (GDK_MACOS_SURFACE (surface))))
+ [(GdkMacosView *)nsview setOpaqueRegion:region];
}
static void
@@ -133,12 +133,16 @@ gdk_macos_surface_hide (GdkSurface *surface)
GdkMacosSurface *self = (GdkMacosSurface *)surface;
GdkSeat *seat;
gboolean was_mapped;
+ gboolean was_key;
g_assert (GDK_IS_MACOS_SURFACE (self));
+ self->show_on_next_swap = FALSE;
+
_gdk_macos_display_remove_frame_callback (GDK_MACOS_DISPLAY (surface->display), self);
was_mapped = GDK_SURFACE_IS_MAPPED (GDK_SURFACE (self));
+ was_key = [self->window isKeyWindow];
seat = gdk_display_get_default_seat (surface->display);
gdk_seat_ungrab (seat);
@@ -147,6 +151,20 @@ gdk_macos_surface_hide (GdkSurface *surface)
_gdk_surface_clear_update_area (surface);
+ g_clear_object (&self->buffer);
+ g_clear_object (&self->front);
+
+ if (was_key)
+ {
+ /* Return key input to the parent window if necessary */
+ if (surface->parent != NULL && GDK_SURFACE_IS_MAPPED (surface->parent))
+ {
+ GdkMacosWindow *parentWindow = GDK_MACOS_SURFACE (surface->parent)->window;
+
+ [parentWindow showAndMakeKey:YES];
+ }
+ }
+
if (was_mapped)
gdk_surface_freeze_updates (GDK_SURFACE (self));
}
@@ -410,6 +428,9 @@ gdk_macos_surface_destroy (GdkSurface *surface,
g_clear_pointer (&self->monitors, g_ptr_array_unref);
+ g_clear_object (&self->buffer);
+ g_clear_object (&self->front);
+
g_assert (self->sorted.prev == NULL);
g_assert (self->sorted.next == NULL);
g_assert (self->frame.prev == NULL);
@@ -763,6 +784,9 @@ _gdk_macos_surface_configure (GdkMacosSurface *self)
surface->width = content_rect.size.width;
surface->height = content_rect.size.height;
+ g_clear_object (&self->buffer);
+ g_clear_object (&self->front);
+
_gdk_surface_update_size (surface);
gdk_surface_request_layout (surface);
gdk_surface_invalidate_rect (surface, NULL);
@@ -823,7 +847,7 @@ _gdk_macos_surface_show (GdkMacosSurface *self)
_gdk_macos_display_clear_sorting (GDK_MACOS_DISPLAY (GDK_SURFACE (self)->display));
- [self->window showAndMakeKey:YES];
+ self->show_on_next_swap = TRUE;
if (!was_mapped)
{
@@ -833,51 +857,6 @@ _gdk_macos_surface_show (GdkMacosSurface *self)
gdk_surface_thaw_updates (GDK_SURFACE (self));
}
}
-
- [[self->window contentView] setNeedsDisplay:YES];
-}
-
-CGContextRef
-_gdk_macos_surface_acquire_context (GdkMacosSurface *self,
- gboolean clear_scale,
- gboolean antialias)
-{
- CGContextRef cg_context;
-
- g_return_val_if_fail (GDK_IS_MACOS_SURFACE (self), NULL);
-
- if (GDK_SURFACE_DESTROYED (self))
- return NULL;
-
- if (!(cg_context = [[NSGraphicsContext currentContext] CGContext]))
- return NULL;
-
- CGContextSaveGState (cg_context);
-
- if (!antialias)
- CGContextSetAllowsAntialiasing (cg_context, antialias);
-
- if (clear_scale)
- {
- CGSize scale;
-
- scale = CGSizeMake (1.0, 1.0);
- scale = CGContextConvertSizeToDeviceSpace (cg_context, scale);
-
- CGContextScaleCTM (cg_context, 1.0 / fabs (scale.width), 1.0 / fabs (scale.height));
- }
-
- return cg_context;
-}
-
-void
-_gdk_macos_surface_release_context (GdkMacosSurface *self,
- CGContextRef cg_context)
-{
- g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
-
- CGContextRestoreGState (cg_context);
- CGContextSetAllowsAntialiasing (cg_context, TRUE);
}
void
@@ -1056,6 +1035,10 @@ _gdk_macos_surface_monitor_changed (GdkMacosSurface *self)
g_object_unref (monitor);
}
+ /* We need to create a new IOSurface for this monitor */
+ g_clear_object (&self->buffer);
+ g_clear_object (&self->front);
+
_gdk_macos_surface_configure (self);
gdk_surface_invalidate_rect (GDK_SURFACE (self), NULL);
@@ -1139,3 +1122,66 @@ _gdk_macos_surface_get_root_coords (GdkMacosSurface *self,
if (y)
*y = out_y;
}
+
+GdkMacosBuffer *
+_gdk_macos_surface_get_buffer (GdkMacosSurface *self)
+{
+ g_return_val_if_fail (GDK_IS_MACOS_SURFACE (self), NULL);
+
+ if (GDK_SURFACE_DESTROYED (self))
+ return NULL;
+
+ if (self->buffer == NULL)
+ {
+ /* Create replacement buffer. We always use 4-byte and 32-bit BGRA for
+ * our surface as that can work with both Cairo and GL. The GdkMacosTile
+ * handles opaque regions for the compositor, so using 3-byte/24-bit is
+ * not a necessary optimization.
+ */
+ double scale = gdk_surface_get_scale_factor (GDK_SURFACE (self));
+ guint width = GDK_SURFACE (self)->width * scale;
+ guint height = GDK_SURFACE (self)->height * scale;
+
+ self->buffer = _gdk_macos_buffer_new (width, height, scale, 4, 32);
+ }
+
+ return self->buffer;
+}
+
+static void
+_gdk_macos_surface_do_delayed_show (GdkMacosSurface *self)
+{
+ g_assert (GDK_IS_MACOS_SURFACE (self));
+
+ self->show_on_next_swap = FALSE;
+ [self->window showAndMakeKey:YES];
+ gdk_surface_request_motion (GDK_SURFACE (self));
+}
+
+void
+_gdk_macos_surface_swap_buffers (GdkMacosSurface *self,
+ const cairo_region_t *damage)
+{
+ GdkMacosBuffer *swap;
+
+ g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
+ g_return_if_fail (damage != NULL);
+
+ swap = self->buffer;
+ self->buffer = self->front;
+ self->front = swap;
+
+ /* This code looks like it swaps buffers, but since the IOSurfaceRef
+ * appears to be retained on the other side, we really just ask all
+ * of the GdkMacosTile CALayer's to update their contents.
+ */
+ [self->window swapBuffer:swap withDamage:damage];
+
+ /* We might have delayed actually showing the window until the buffer
+ * contents are ready to be displayed. Doing so ensures that we don't
+ * get a point where we might have invalid buffer contents before we
+ * have content to display to the user.
+ */
+ if G_UNLIKELY (self->show_on_next_swap)
+ _gdk_macos_surface_do_delayed_show (self);
+}
diff --git a/gdk/macos/meson.build b/gdk/macos/meson.build
index 3a10dbf944..86f20bd2ea 100644
--- a/gdk/macos/meson.build
+++ b/gdk/macos/meson.build
@@ -2,6 +2,7 @@ gdk_macos_sources = files([
'edgesnapping.c',
'gdkdisplaylinksource.c',
+ 'gdkmacosbuffer.c',
'gdkmacoscairocontext.c',
'gdkmacosclipboard.c',
'gdkmacoscursor.c',
@@ -20,11 +21,10 @@ gdk_macos_sources = files([
'gdkmacosseat.c',
'gdkmacossurface.c',
'gdkmacostoplevelsurface.c',
-
'GdkMacosBaseView.c',
- 'GdkMacosCairoView.c',
- 'GdkMacosCairoSubview.c',
- 'GdkMacosGLView.c',
+ 'GdkMacosLayer.c',
+ 'GdkMacosTile.c',
+ 'GdkMacosView.c',
'GdkMacosWindow.c',
])
@@ -46,6 +46,7 @@ gdk_macos_frameworks = [
'CoreVideo',
'CoreServices',
'Foundation',
+ 'IOSurface',
'OpenGL',
'QuartzCore',
]
diff --git a/gsk/gl/gskglcommandqueue.c b/gsk/gl/gskglcommandqueue.c
index 3af8c1eb09..b7640edf87 100644
--- a/gsk/gl/gskglcommandqueue.c
+++ b/gsk/gl/gskglcommandqueue.c
@@ -675,9 +675,9 @@ gsk_gl_command_queue_split_draw (GskGLCommandQueue *self)
}
void
-gsk_gl_command_queue_clear (GskGLCommandQueue *self,
- guint clear_bits,
- const graphene_rect_t *viewport)
+gsk_gl_command_queue_clear (GskGLCommandQueue *self,
+ guint clear_bits,
+ const graphene_rect_t *viewport)
{
GskGLCommandBatch *batch;
@@ -750,11 +750,12 @@ static inline void
apply_scissor (gboolean *state,
guint framebuffer,
const graphene_rect_t *scissor,
- gboolean has_scissor)
+ gboolean has_scissor,
+ guint default_framebuffer)
{
g_assert (framebuffer != (guint)-1);
- if (framebuffer != 0 || !has_scissor)
+ if (framebuffer != default_framebuffer || !has_scissor)
{
if (*state != FALSE)
{
@@ -935,15 +936,24 @@ gsk_gl_command_queue_sort_batches (GskGLCommandQueue *self)
* @self: a `GskGLCommandQueue`
* @surface_height: the height of the backing surface
* @scale_factor: the scale factor of the backing surface
- * #scissor: (nullable): the scissor clip if any
+ * @scissor: (nullable): the scissor clip if any
+ * @default_framebuffer: the default framebuffer id if not zero
*
* Executes all of the batches in the command queue.
+ *
+ * Typically, the scissor rect is only applied when rendering to the default
+ * framebuffer (zero in most cases). However, if @default_framebuffer is not
+ * zero, it will be checked to see if the rendering target matches so that
+ * the scissor rect is applied. This should be used in cases where rendering
+ * to the backbuffer for display is not the default GL framebuffer of zero.
+ * Currently, this happens when rendering on macOS using IOSurface.
*/
void
gsk_gl_command_queue_execute (GskGLCommandQueue *self,
guint surface_height,
guint scale_factor,
- const cairo_region_t *scissor)
+ const cairo_region_t *scissor,
+ guint default_framebuffer)
{
G_GNUC_UNUSED guint count = 0;
graphene_rect_t scissor_test;
@@ -1049,7 +1059,7 @@ gsk_gl_command_queue_execute (GskGLCommandQueue *self,
case GSK_GL_COMMAND_KIND_CLEAR:
if (apply_framebuffer (&framebuffer, batch->clear.framebuffer))
{
- apply_scissor (&scissor_state, framebuffer, &scissor_test, has_scissor);
+ apply_scissor (&scissor_state, framebuffer, &scissor_test, has_scissor, default_framebuffer);
n_fbos++;
}
@@ -1073,7 +1083,7 @@ gsk_gl_command_queue_execute (GskGLCommandQueue *self,
if (apply_framebuffer (&framebuffer, batch->draw.framebuffer))
{
- apply_scissor (&scissor_state, framebuffer, &scissor_test, has_scissor);
+ apply_scissor (&scissor_state, framebuffer, &scissor_test, has_scissor, default_framebuffer);
n_fbos++;
}
diff --git a/gsk/gl/gskglcommandqueueprivate.h b/gsk/gl/gskglcommandqueueprivate.h
index 4147283e6d..48911305a8 100644
--- a/gsk/gl/gskglcommandqueueprivate.h
+++ b/gsk/gl/gskglcommandqueueprivate.h
@@ -278,7 +278,8 @@ void gsk_gl_command_queue_end_frame (GskGLCommandQueue
void gsk_gl_command_queue_execute (GskGLCommandQueue *self,
guint surface_height,
guint scale_factor,
- const cairo_region_t *scissor);
+ const cairo_region_t *scissor,
+ guint default_framebuffer);
int gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self,
GdkTexture *texture,
int min_filter,
diff --git a/gsk/gl/gskglrenderjob.c b/gsk/gl/gskglrenderjob.c
index 65b5adf84d..afefe7a018 100644
--- a/gsk/gl/gskglrenderjob.c
+++ b/gsk/gl/gskglrenderjob.c
@@ -123,6 +123,7 @@ struct _GskGLRenderJob
* GL context.
*/
guint framebuffer;
+ guint default_framebuffer;
/* The viewport we are using. This state is updated as we process render
* nodes in the specific visitor callbacks.
@@ -4058,7 +4059,7 @@ gsk_gl_render_job_render_flipped (GskGLRenderJob *job,
gsk_gl_render_job_end_draw (job);
gdk_gl_context_push_debug_group (job->command_queue->context, "Executing command queue");
- gsk_gl_command_queue_execute (job->command_queue, surface_height, 1, NULL);
+ gsk_gl_command_queue_execute (job->command_queue, surface_height, 1, NULL, job->default_framebuffer);
gdk_gl_context_pop_debug_group (job->command_queue->context);
glDeleteFramebuffers (1, &framebuffer_id);
@@ -4108,7 +4109,7 @@ gsk_gl_render_job_render (GskGLRenderJob *job,
start_time = GDK_PROFILER_CURRENT_TIME;
gsk_gl_command_queue_make_current (job->command_queue);
gdk_gl_context_push_debug_group (job->command_queue->context, "Executing command queue");
- gsk_gl_command_queue_execute (job->command_queue, surface_height, scale_factor, job->region);
+ gsk_gl_command_queue_execute (job->command_queue, surface_height, scale_factor, job->region, job->default_framebuffer);
gdk_gl_context_pop_debug_group (job->command_queue->context);
gdk_profiler_add_mark (start_time, GDK_PROFILER_CURRENT_TIME-start_time, "Execute GL command queue", "");
}
@@ -4157,11 +4158,23 @@ gsk_gl_render_job_new (GskGLDriver *driver,
const graphene_rect_t *clip_rect = viewport;
graphene_rect_t transformed_extents;
GskGLRenderJob *job;
+ GdkGLContext *context;
+ GLint default_framebuffer = 0;
g_return_val_if_fail (GSK_IS_GL_DRIVER (driver), NULL);
g_return_val_if_fail (viewport != NULL, NULL);
g_return_val_if_fail (scale_factor > 0, NULL);
+ /* Check for non-standard framebuffer binding as we might not be using
+ * the default framebuffer on systems like macOS where we've bound an
+ * IOSurface to a GL_TEXTURE_RECTANGLE. Otherwise, no scissor clip will
+ * be applied in the command queue causing overdrawing.
+ */
+ context = driver->command_queue->context;
+ default_framebuffer = GDK_GL_CONTEXT_GET_CLASS (context)->get_default_framebuffer (context);
+ if (framebuffer == 0 && default_framebuffer != 0)
+ framebuffer = default_framebuffer;
+
job = g_slice_new0 (GskGLRenderJob);
job->driver = g_object_ref (driver);
job->command_queue = job->driver->command_queue;
@@ -4169,6 +4182,7 @@ gsk_gl_render_job_new (GskGLDriver *driver,
job->modelview = g_array_sized_new (FALSE, FALSE, sizeof (GskGLRenderModelview), 16);
job->framebuffer = framebuffer;
job->clear_framebuffer = !!clear_framebuffer;
+ job->default_framebuffer = default_framebuffer;
job->offset_x = 0;
job->offset_y = 0;
job->scale_x = scale_factor;
diff --git a/gtk/gtkfilechoosernativequartz.c b/gtk/gtkfilechoosernativequartz.c
index c004167a23..3854937c0d 100644
--- a/gtk/gtkfilechoosernativequartz.c
+++ b/gtk/gtkfilechoosernativequartz.c
@@ -300,7 +300,7 @@ filechooser_quartz_launch (FileChooserQuartzData *data)
if (data->filters)
{
// when filters have been provided, a combobox needs to be added
- data->filter_combo_box = [[NSComboBox alloc] initWithFrame:NSMakeRect(0.0, 0.0, 200, 20)];
+ data->filter_combo_box = [[NSComboBox alloc] initWithFrame:NSMakeRect(0, 0, 200, 24)];
[data->filter_combo_box addItemsWithObjectValues:data->filter_names];
[data->filter_combo_box setEditable:NO];
[data->filter_combo_box setDelegate:[[FilterComboBox alloc] initWithData:data]];