+++ /dev/null
-/* 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 <http://www.gnu.org/licenses/>.
- *
- * SPDX-License-Identifier: LGPL-2.1-or-later
- */
-
-#include "config.h"
-
-#include <CoreGraphics/CoreGraphics.h>
-#include <cairo-quartz.h>
-
-#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
+++ /dev/null
-/* GdkMacosCairoSubview.h
- *
- * 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 <http://www.gnu.org/licenses/>.
- *
- * SPDX-License-Identifier: LGPL-2.1-or-later
- */
-
-#include <AppKit/AppKit.h>
-#include <cairo.h>
-#include <glib.h>
-
-#define GDK_IS_MACOS_CAIRO_SUBVIEW(obj) ((obj) && [obj isKindOfClass:[GdkMacosCairoSubview class]])
-
-@interface GdkMacosCairoSubview : NSView
-{
- 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;
-
-@end
+++ /dev/null
-/* 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 <http://www.gnu.org/licenses/>.
- *
- * SPDX-License-Identifier: LGPL-2.1-or-later
- */
-
-#include "config.h"
-
-#include <CoreGraphics/CoreGraphics.h>
-#include <cairo-quartz.h>
-
-
-#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
+++ /dev/null
-/* GdkMacosCairoView.h
- *
- * 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 <http://www.gnu.org/licenses/>.
- *
- * SPDX-License-Identifier: LGPL-2.1-or-later
- */
-
-#include <cairo.h>
-
-#import "GdkMacosBaseView.h"
-
-#define GDK_IS_MACOS_CAIRO_VIEW(obj) ((obj) && [obj isKindOfClass:[GdkMacosCairoView class]])
-
-@interface GdkMacosCairoView : GdkMacosBaseView
-{
- NSView *transparent;
- GPtrArray *opaque;
-}
-
--(void)setCairoSurface:(cairo_surface_t *)cairoSurface
- withDamage:(cairo_region_t *)region;
-
-@end
+++ /dev/null
-/* GdkMacosGLView.c
- *
- * Copyright 2020 Christian Hergert <chergert@redhat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library. If not, see <http://www.gnu.org/licenses/>.
- *
- * SPDX-License-Identifier: LGPL-2.1-or-later
- */
-
-#include "config.h"
-
-#include <CoreGraphics/CoreGraphics.h>
-#include <OpenGL/gl.h>
-
-#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
+++ /dev/null
-/* GdkMacosGLView.h
- *
- * 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 <http://www.gnu.org/licenses/>.
- *
- * SPDX-License-Identifier: LGPL-2.1-or-later
- */
-
-#include <cairo.h>
-
-#import "GdkMacosBaseView.h"
-
-#define GDK_IS_MACOS_GL_VIEW(obj) ((obj) && [obj isKindOfClass:[GdkMacosGLView class]])
-
-G_GNUC_BEGIN_IGNORE_DEPRECATIONS
-
-@interface GdkMacosGLView : GdkMacosBaseView
-{
- NSOpenGLContext *_openGLContext;
-}
-
--(void)setOpenGLContext:(NSOpenGLContext*)context;
--(NSOpenGLContext *)openGLContext;
--(void)invalidateRegion:(const cairo_region_t *)region;
-
-G_GNUC_END_IGNORE_DEPRECATIONS
-
-@end
--- /dev/null
+/* GdkMacosLayer.c
+ *
+ * Copyright © 2022 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#import "GdkMacosLayer.h"
+#import "GdkMacosTile.h"
+
+@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<CanSetContentsOpaque>)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 (CGRectEqualToRect (frame, self.frame))
+ return;
+
+ self->_layoutInvalid = TRUE;
+
+ [super setFrame:frame];
+ [self setNeedsLayout];
+}
+
+-(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
--- /dev/null
+/* GdkMacosLayer.h
+ *
+ * Copyright © 2022 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include <QuartzCore/QuartzCore.h>
+#include <IOSurface/IOSurface.h>
+
+#include <cairo.h>
+#include <glib.h>
+
+#include "gdkmacosbuffer-private.h"
+
+#define GDK_IS_MACOS_LAYER(obj) ((obj) && [obj isKindOfClass:[GdkMacosLayer class]])
+
+@interface GdkMacosLayer : CALayer
+{
+ cairo_region_t *_opaqueRegion;
+ GArray *_tiles;
+ guint _opaque : 1;
+ guint _layoutInvalid : 1;
+ guint _inSwapBuffer : 1;
+ guint _isFlipped : 1;
+};
+
+-(void)setOpaqueRegion:(const cairo_region_t *)opaqueRegion;
+-(void)swapBuffer:(GdkMacosBuffer *)buffer withDamage:(const cairo_region_t *)damage;
+
+@end
--- /dev/null
+/* GdkMacosTile.c
+ *
+ * Copyright © 2022 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include <AppKit/AppKit.h>
+
+#import "GdkMacosTile.h"
+
+@implementation GdkMacosTile
+
+-(id)init
+{
+ self = [super init];
+
+ [self setContentsScale:1.0];
+ [self setEdgeAntialiasingMask:0];
+ [self setDrawsAsynchronously:YES];
+
+ return self;
+}
+
+-(void)swapBuffer:(IOSurfaceRef)buffer withRect:(CGRect)rect
+{
+ if G_LIKELY ([self contents] == (id)buffer)
+ [(id<CanSetContentsChanged>)self setContentsChanged];
+ else
+ [self setContents:(id)buffer];
+
+ if G_UNLIKELY (!CGRectEqualToRect ([self contentsRect], rect))
+ self.contentsRect = rect;
+}
+
+@end
--- /dev/null
+/* GdkMacosTile.h
+ *
+ * Copyright © 2022 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include <QuartzCore/QuartzCore.h>
+
+#include "gdkmacosbuffer-private.h"
+
+#define GDK_IS_MACOS_TILE(obj) ((obj) && [obj isKindOfClass:[GdkMacosTile class]])
+
+@protocol CanSetContentsChanged
+-(void)setContentsChanged;
+@end
+
+@interface GdkMacosTile : CALayer
+{
+};
+
+-(void)swapBuffer:(IOSurfaceRef)buffer withRect:(CGRect)rect;
+
+@end
--- /dev/null
+/* GdkMacosView.c
+ *
+ * Copyright 2022 Christian Hergert <chergert@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include <CoreGraphics/CoreGraphics.h>
+
+#import "GdkMacosLayer.h"
+#import "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
--- /dev/null
+/* GdkMacosView.h
+ *
+ * Copyright 2022 Christian Hergert <chergert@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include <cairo.h>
+
+#import "GdkMacosBaseView.h"
+
+#include "gdkmacosbuffer-private.h"
+
+#define GDK_IS_MACOS_VIEW(obj) ((obj) && [obj isKindOfClass:[GdkMacosView class]])
+
+@interface GdkMacosView : GdkMacosBaseView
+{
+ NSRect _nextFrame;
+ guint _nextFrameDirty : 1;
+}
+
+-(void)setOpaqueRegion:(const cairo_region_t *)opaqueRegion;
+-(void)swapBuffer:(GdkMacosBuffer *)buffer withDamage:(const cairo_region_t *)damage;
+
+@end
#include <gdk/gdk.h>
#import "GdkMacosBaseView.h"
-#import "GdkMacosCairoView.h"
-#import "GdkMacosGLView.h"
+#import "GdkMacosView.h"
#import "GdkMacosWindow.h"
#include "gdkmacosclipboard-private.h"
_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;
}
defer:(BOOL)flag
screen:(NSScreen *)screen
{
- GdkMacosCairoView *view;
+ GdkMacosView *view;
self = [super initWithContentRect:contentRect
styleMask:styleMask
[self setAcceptsMouseMovedEvents:YES];
[self setDelegate:(id<NSWindowDelegate>)self];
[self setReleasedWhenClosed:YES];
+ [self setPreservesContentDuringLiveResize:NO];
- view = [[GdkMacosCairoView alloc] initWithFrame:contentRect];
+ view = [[GdkMacosView alloc] initWithFrame:contentRect];
[self setContentView:view];
[view release];
-(void)windowWillExitFullScreen:(NSNotification *)aNotification
{
- [self setFrame:lastUnfullscreenFrame display:YES];
+ [self setFrame:lastUnfullscreenFrame display:NO];
}
-(void)windowDidExitFullScreen:(NSNotification *)aNotification
return NO;
}
+-(void)swapBuffer:(GdkMacosBuffer *)buffer withDamage:(const cairo_region_t *)damage
+{
+ [(GdkMacosView *)[self contentView] swapBuffer:buffer withDamage:damage];
+}
+
@end
#import <AppKit/AppKit.h>
#import <Foundation/Foundation.h>
+#import <IOSurface/IOSurface.h>
#include <gdk/gdk.h>
+#include "gdkmacosbuffer-private.h"
#include "gdkmacosdisplay.h"
#include "gdkmacossurface.h"
#include "edgesnapping.h"
-(BOOL)trackManualMove;
-(BOOL)trackManualResize;
-(void)setDecorated:(BOOL)decorated;
+-(void)swapBuffer:(GdkMacosBuffer *)buffer withDamage:(const cairo_region_t *)damage;
@end
--- /dev/null
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef __GDK_MACOS_BUFFER_PRIVATE_H__
+#define __GDK_MACOS_BUFFER_PRIVATE_H__
+
+#include <CoreGraphics/CoreGraphics.h>
+#include <Foundation/Foundation.h>
+#include <IOSurface/IOSurface.h>
+
+#include <cairo.h>
+#include <glib-object.h>
+
+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__ */
--- /dev/null
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include <IOSurface/IOSurface.h>
+#include <Foundation/Foundation.h>
+#include <OpenGL/CGLIOSurface.h>
+#include <QuartzCore/QuartzCore.h>
+
+#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;
+}
#include "gdkconfig.h"
+#include <cairo.h>
+#include <QuartzCore/QuartzCore.h>
#include <CoreGraphics/CoreGraphics.h>
-#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
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;
-
- g_assert (GDK_IS_MACOS_SURFACE (surface));
-
- /* We use a cairo image surface here instead of a quartz surface because
- * we get strange artifacts with the quartz surface such as empty
- * cross-fades when hovering buttons. For performance, we want to be using
- * GL rendering so there isn't much point here as correctness is better.
- *
- * Additionally, so we can take avantage of faster paths in Core
- * Graphics, we want our data pointer to be 16-byte aligned and our rows
- * to be 16-byte aligned or we risk errors below us. Normally, cairo
- * image surface does not guarantee the later, which means we could end
- * up doing some costly copies along the way to compositing.
- */
+ GdkMacosBuffer *buffer = data;
+
+ g_assert (GDK_IS_MACOS_BUFFER (buffer));
- 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;
+ _gdk_macos_buffer_unlock (buffer);
+ g_clear_object (&buffer);
}
static cairo_t *
-do_cairo_create (GdkMacosCairoContext *self)
+_gdk_macos_cairo_context_cairo_create (GdkCairoContext *cairo_context)
{
+ GdkMacosCairoContext *self = (GdkMacosCairoContext *)cairo_context;
+ 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));
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;
-}
-
-static cairo_t *
-_gdk_macos_cairo_context_cairo_create (GdkCairoContext *cairo_context)
-{
- GdkMacosCairoContext *self = (GdkMacosCairoContext *)cairo_context;
+ nswindow = _gdk_macos_surface_get_native (GDK_MACOS_SURFACE (surface));
+ opaque = [nswindow isOpaque];
+
+ 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);
+ }
- g_assert (GDK_IS_MACOS_CAIRO_CONTEXT (self));
+ /* 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);
+ }
- if (self->cr != NULL)
- return cairo_reference (self->cr);
+failure:
+ cairo_surface_destroy (image_surface);
- return do_cairo_create (self);
+ return cr;
}
static void
cairo_region_t *region)
{
GdkMacosCairoContext *self = (GdkMacosCairoContext *)draw_context;
+ GdkMacosBuffer *buffer;
GdkSurface *surface;
- NSWindow *nswindow;
g_assert (GDK_IS_MACOS_CAIRO_CONTEXT (self));
- surface = gdk_draw_context_get_surface (draw_context);
- nswindow = _gdk_macos_surface_get_native (GDK_MACOS_SURFACE (surface));
-
- if (self->window_surface == NULL)
- self->window_surface = create_cairo_surface_for_surface (surface);
+ [CATransaction begin];
+ [CATransaction setDisableActions:YES];
- self->cr = do_cairo_create (self);
+ surface = gdk_draw_context_get_surface (draw_context);
+ buffer = _gdk_macos_surface_get_buffer (GDK_MACOS_SURFACE (surface));
- 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 (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_assert (GDK_IS_MACOS_CAIRO_CONTEXT (draw_context));
- 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;
#include "gdkmacossurface.h"
#import <OpenGL/OpenGL.h>
-#import <OpenGL/gl.h>
+#import <OpenGL/gl3.h>
#import <AppKit/AppKit.h>
G_BEGIN_DECLS
{
GdkGLContext parent_instance;
+ cairo_region_t *damage;
+
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
- NSOpenGLContext *gl_context;
+ CGLContextObj cgl_context;
G_GNUC_END_IGNORE_DEPRECATIONS
- NSWindow *dummy_window;
- NSView *dummy_view;
+ GLuint texture;
+ GLuint target;
+ GLuint fbo;
- cairo_region_t *damage;
-
- guint is_attached : 1;
- guint needs_resize : 1;
+ guint last_opaque : 1;
};
struct _GdkMacosGLContextClass
GdkGLContextClass parent_class;
};
-
G_END_DECLS
#endif /* __GDK_MACOS_GL_CONTEXT_PRIVATE_H__ */
#include "config.h"
+#include "gdkconfig.h"
+
+#include <OpenGL/gl3.h>
+#include <OpenGL/CGLIOSurface.h>
+#include <QuartzCore/QuartzCore.h>
+
+#include "gdkmacosbuffer-private.h"
#include "gdkmacosglcontext-private.h"
#include "gdkmacossurface-private.h"
-#include "gdkmacostoplevelsurface-private.h"
-#include "gdkintl.h"
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
-#include <OpenGL/gl.h>
+G_DEFINE_TYPE (GdkMacosGLContext, gdk_macos_gl_context, GDK_TYPE_GL_CONTEXT)
-#import "GdkMacosGLView.h"
+#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;
+ }
-G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+ return TRUE;
+}
-G_DEFINE_TYPE (GdkMacosGLContext, gdk_macos_gl_context, GDK_TYPE_GL_CONTEXT)
+/* 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)
}
}
-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));
-
- if (self->gl_context == nil)
+ GLuint texture = 0;
+
+ 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 *
-create_pixel_format (int major,
- int minor,
- GError **error)
+static void
+gdk_macos_gl_context_allocate (GdkMacosGLContext *self)
{
- NSOpenGLPixelFormatAttribute attrs[] = {
- NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersionLegacy,
- NSOpenGLPFAAccelerated,
- NSOpenGLPFADoubleBuffer,
- NSOpenGLPFABackingStore,
- NSOpenGLPFAColorSize, 24,
- NSOpenGLPFAAlphaSize, 8,
- 0
- };
+ GdkSurface *surface;
+ GLint opaque;
- if (major == 3 && minor == 2)
- attrs[1] = NSOpenGLProfileVersion3_2Core;
- else if (major == 4 && minor == 1)
- attrs[1] = NSOpenGLProfileVersion4_1Core;
+ 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);
- NSOpenGLPixelFormat *format = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
+ if (!(surface = gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (self))))
+ return;
- if (format == NULL)
- g_set_error (error,
- GDK_GL_ERROR,
- GDK_GL_ERROR_NOT_AVAILABLE,
- "Failed to create pixel format");
+ /* 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;
+ }
- return g_steal_pointer (&format);
+ 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 NSView *
-ensure_gl_view (GdkMacosGLContext *self)
+static void
+gdk_macos_gl_context_release (GdkMacosGLContext *self)
{
- GdkMacosSurface *surface;
- NSWindow *nswindow;
- NSView *nsview;
-
g_assert (GDK_IS_MACOS_GL_CONTEXT (self));
+ g_assert (self->texture != 0 || self->fbo == 0);
+ g_assert (self->fbo != 0 || self->texture == 0);
- 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);
+ glBindFramebuffer (GL_FRAMEBUFFER, 0);
+ glActiveTexture (GL_TEXTURE0);
+ glBindTexture (self->target, 0);
- if G_UNLIKELY (!GDK_IS_MACOS_GL_VIEW (nsview))
+ if (self->fbo != 0)
{
- 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];
- }
+ glDeleteFramebuffers (1, &self->fbo);
+ self->fbo = 0;
+ }
- if (self->dummy_window != NULL)
- {
- NSWindow *dummy_window = g_steal_pointer (&self->dummy_window);
- [dummy_window release];
- }
+ if (self->texture != 0)
+ {
+ glDeleteTextures (1, &self->texture);
+ self->texture = 0;
}
+}
+
+static CGLPixelFormatObj
+create_pixel_format (int major,
+ int minor,
+ GError **error)
+{
+ CGLPixelFormatAttribute attrs[] = {
+ kCGLPFAOpenGLProfile, (CGLPixelFormatAttribute)kCGLOGLPVersion_Legacy,
+ kCGLPFAAllowOfflineRenderers, /* allow sharing across GPUs */
+ kCGLPFAColorSize, 24,
+ kCGLPFAAlphaSize, 8,
+ 0
+ };
+ CGLPixelFormatObj format = NULL;
+ GLint n_format = 1;
+
+ if (major == 3 && minor == 2)
+ attrs[1] = (CGLPixelFormatAttribute)kCGLOGLPVersion_GL3_Core;
+ else if (major == 4 && minor == 1)
+ attrs[1] = (CGLPixelFormatAttribute)kCGLOGLPVersion_GL4_Core;
- return [nswindow contentView];
+ if (!CHECK (error, CGLChoosePixelFormat (attrs, &format, &n_format)))
+ return NULL;
+
+ return g_steal_pointer (&format);
}
static GdkGLAPI
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;
+ if (validate)
+ CHECK (NULL, CGLEnable (cgl_context, kCGLCEStateValidation));
- CGLSetParameter (cgl_context, kCGLCPSwapRectangle, swapRect);
- CGLSetParameter (cgl_context, kCGLCPSwapInterval, &sync_to_framerate);
+ if (!CHECK (error, CGLSetParameter (cgl_context, kCGLCPSwapInterval, &sync_to_framerate)) ||
+ !CHECK (error, CGLGetParameter (cgl_context, kCGLCPCurrentRendererID, &renderer_id)))
+ {
+ CGLReleaseContext (cgl_context);
+ return 0;
+ }
- CGLEnable (cgl_context, kCGLCESwapRectangle);
- if (validate)
- CGLEnable (cgl_context, kCGLCEStateValidation);
+ surface = gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (context));
- 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 (surface != NULL)
+ {
+ /* 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);
+ }
- 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,
+ g_message ("Created CGLContextObj@%p using %s",
+ cgl_context,
get_renderer_name (renderer_id)));
- self->gl_context = g_steal_pointer (&gl_context);
+ self->cgl_context = g_steal_pointer (&cgl_context);
if (existing != NULL)
- [existing makeCurrentContext];
+ CGLSetCurrentContext (existing);
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)
- {
- 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;
- }
-
- return FALSE;
-}
-
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));
- g_clear_pointer (&self->damage, cairo_region_destroy);
- self->damage = cairo_region_copy (painted);
-
- /* 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;
+ _gdk_macos_buffer_set_flipped (buffer, TRUE);
- self->needs_resize = FALSE;
+ /* Create our render target and bind it */
+ gdk_gl_context_make_current (GDK_GL_CONTEXT (self));
+ gdk_macos_gl_context_allocate (self);
- 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;
+ GDK_DRAW_CONTEXT_CLASS (gdk_macos_gl_context_parent_class)->begin_frame (context, prefers_high_depth, region);
- /* 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));
+ g_clear_pointer (&self->damage, cairo_region_destroy);
+ self->damage = g_steal_pointer (©);
- [(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
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];
-
- /* 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);
-
- [self->gl_context flushBuffer];
- }
+ 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 (self->cgl_context, kCGLCPSwapRectangle, swapRect);
+
+ 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
g_assert (GDK_IS_MACOS_GL_CONTEXT (self));
- self->needs_resize = TRUE;
-
g_clear_pointer (&self->damage, cairo_region_destroy);
}
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;
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;
{
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)
- {
- NSView *nsview = g_steal_pointer (&self->dummy_view);
- [nsview release];
- }
-
- if (self->dummy_window != nil)
- {
- NSWindow *nswindow = g_steal_pointer (&self->dummy_window);
- [nswindow release];
- }
+ self->texture = 0;
+ self->fbo = 0;
- if (self->gl_context != nil)
+ if (self->cgl_context != nil)
{
- NSOpenGLContext *gl_context = g_steal_pointer (&self->gl_context);
+ CGLContextObj cgl_context = g_steal_pointer (&self->cgl_context);
- if (gl_context == [NSOpenGLContext currentContext])
- [NSOpenGLContext clearCurrentContext];
+ if (cgl_context == CGLGetCurrentContext ())
+ CGLSetCurrentContext (NULL);
- [gl_context clearDrawable];
- [gl_context release];
+ CGLClearDrawable (cgl_context);
+ CGLDestroyContext (cgl_context);
}
g_clear_pointer (&self->damage, cairo_region_destroy);
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;
}
static void
gdk_macos_gl_context_init (GdkMacosGLContext *self)
{
+ self->target = GL_TEXTURE_RECTANGLE;
}
G_GNUC_END_IGNORE_DEPRECATIONS
#include "gdksurfaceprivate.h"
+#include "gdkmacosbuffer-private.h"
#include "gdkmacosdisplay.h"
#include "gdkmacossurface.h"
GList frame;
GdkMacosWindow *window;
+ GdkMacosBuffer *buffer;
GPtrArray *monitors;
- cairo_region_t *input_region;
cairo_region_t *opaque_region;
char *title;
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
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
#include <float.h>
#include <gdk/gdk.h>
-#import "GdkMacosCairoView.h"
+#import "GdkMacosView.h"
#include "gdkmacossurface-private.h"
return ([self->window styleMask] & NSWindowStyleMaskFullScreen) != 0;
}
+
void
_gdk_macos_surface_reposition_children (GdkMacosSurface *self)
{
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
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));
_gdk_surface_clear_update_area (surface);
+ g_clear_object (&self->buffer);
+
if (was_mapped)
gdk_surface_freeze_updates (GDK_SURFACE (self));
}
surface->width = content_rect.size.width;
surface->height = content_rect.size.height;
+ g_clear_object (&self->buffer);
+
_gdk_surface_update_size (surface);
gdk_surface_request_layout (surface);
gdk_surface_invalidate_rect (surface, NULL);
_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)
{
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
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)
+{
+ g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
+ g_return_if_fail (damage != NULL);
+
+ /* 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:self->buffer 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);
+}
'edgesnapping.c',
'gdkdisplaylinksource.c',
+ 'gdkmacosbuffer.c',
'gdkmacoscairocontext.c',
'gdkmacosclipboard.c',
'gdkmacoscursor.c',
'gdkmacosseat.c',
'gdkmacossurface.c',
'gdkmacostoplevelsurface.c',
-
'GdkMacosBaseView.c',
- 'GdkMacosCairoView.c',
- 'GdkMacosCairoSubview.c',
- 'GdkMacosGLView.c',
+ 'GdkMacosLayer.c',
+ 'GdkMacosTile.c',
+ 'GdkMacosView.c',
'GdkMacosWindow.c',
])
'CoreVideo',
'CoreServices',
'Foundation',
+ 'IOSurface',
'OpenGL',
'QuartzCore',
]