forked from AuroraMiddleware/gtk
Merge branch 'wip/chergert/macos-iosurface' into 'main'
macos: modernize rendering with CALayer and IOSurface See merge request GNOME/gtk!4477
This commit is contained in:
commit
2c88797195
@ -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;
|
||||
|
@ -71,6 +71,8 @@ struct _GdkGLContextClass
|
||||
|
||||
gboolean (* is_shared) (GdkGLContext *self,
|
||||
GdkGLContext *other);
|
||||
|
||||
guint (* get_default_framebuffer) (GdkGLContext *self);
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
|
@ -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 <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
|
@ -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 <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
|
@ -1,125 +0,0 @@
|
||||
/* 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
|
386
gdk/macos/GdkMacosLayer.c
Normal file
386
gdk/macos/GdkMacosLayer.c
Normal file
@ -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 <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 (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
|
44
gdk/macos/GdkMacosLayer.h
Normal file
44
gdk/macos/GdkMacosLayer.h
Normal file
@ -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 <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
|
@ -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 <cairo.h>
|
||||
#include "config.h"
|
||||
|
||||
#import "GdkMacosBaseView.h"
|
||||
#include <AppKit/AppKit.h>
|
||||
|
||||
#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<CanSetContentsChanged>)self setContentsChanged];
|
||||
else
|
||||
[self setContents:(id)buffer];
|
||||
|
||||
G_GNUC_END_IGNORE_DEPRECATIONS
|
||||
if G_UNLIKELY (!CGRectEqualToRect ([self contentsRect], rect))
|
||||
self.contentsRect = rect;
|
||||
}
|
||||
|
||||
@end
|
@ -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 <AppKit/AppKit.h>
|
||||
#include <cairo.h>
|
||||
#include <glib.h>
|
||||
#include <QuartzCore/QuartzCore.h>
|
||||
|
||||
#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
|
86
gdk/macos/GdkMacosView.c
Normal file
86
gdk/macos/GdkMacosView.c
Normal file
@ -0,0 +1,86 @@
|
||||
/* 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
|
@ -1,7 +1,6 @@
|
||||
/* GdkMacosCairoView.h
|
||||
/* GdkMacosView.h
|
||||
*
|
||||
* Copyright © 2020 Red Hat, Inc.
|
||||
* Copyright © 2005-2007 Imendio AB
|
||||
* 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
|
||||
@ -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
|
@ -24,8 +24,7 @@
|
||||
#include <gdk/gdk.h>
|
||||
|
||||
#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<NSWindowDelegate>)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
|
||||
|
@ -21,9 +21,11 @@
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <IOSurface/IOSurface.h>
|
||||
|
||||
#include <gdk/gdk.h>
|
||||
|
||||
#include "gdkmacosbuffer-private.h"
|
||||
#include "gdkmacosdisplay.h"
|
||||
#include "gdkmacossurface.h"
|
||||
#include "edgesnapping.h"
|
||||
@ -66,5 +68,6 @@
|
||||
-(BOOL)trackManualMove;
|
||||
-(BOOL)trackManualResize;
|
||||
-(void)setDecorated:(BOOL)decorated;
|
||||
-(void)swapBuffer:(GdkMacosBuffer *)buffer withDamage:(const cairo_region_t *)damage;
|
||||
|
||||
@end
|
||||
|
58
gdk/macos/gdkmacosbuffer-private.h
Normal file
58
gdk/macos/gdkmacosbuffer-private.h
Normal file
@ -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 <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__ */
|
271
gdk/macos/gdkmacosbuffer.c
Normal file
271
gdk/macos/gdkmacosbuffer.c
Normal file
@ -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 <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;
|
||||
}
|
@ -22,19 +22,17 @@
|
||||
|
||||
#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
|
||||
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,7 @@
|
||||
#include "gdkmacossurface.h"
|
||||
|
||||
#import <OpenGL/OpenGL.h>
|
||||
#import <OpenGL/gl.h>
|
||||
#import <OpenGL/gl3.h>
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
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__ */
|
||||
|
@ -19,20 +19,139 @@
|
||||
|
||||
#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"
|
||||
|
||||
#include <OpenGL/gl.h>
|
||||
|
||||
#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
|
||||
|
@ -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
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
||||
#include <float.h>
|
||||
#include <gdk/gdk.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
@ -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',
|
||||
]
|
||||
|
@ -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++;
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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]];
|
||||
|
Loading…
Reference in New Issue
Block a user