(GDK) keep our own record of invalidated rectangles for GdkQuartzView

macOS has broken its exposure/redraw model at least twice by always providing the entire area
of an NSView for every call to drawRect, and cannot provide the list of rects. This change
expands the tracking done using the needs_display_region, and does so directly via the
objective C methods of an NSView (and its derived children like GdkQuartzView).

The result is that when we send an expose signal, GDK/GTK code can obtain an accurate
list of the invalidated rects if desired, and thus avoiding redrawing the entire contents
of a window
This commit is contained in:
Paul Davis 2024-03-18 10:40:42 -06:00
parent 52cce5adc6
commit 9efd521ea6
2 changed files with 136 additions and 27 deletions

View File

@ -602,6 +602,72 @@
gdk_screen_get_rgba_colormap (_gdk_screen);
}
- (void) setNeedsDisplay:(BOOL)yn
{
GdkWindowObject *private;
GdkWindowImplQuartz *impl;
NSRect nsrect = [self bounds];
GdkRectangle r = { nsrect.origin.x, nsrect.origin.y, nsrect.size.width, nsrect.size.height };
private = GDK_WINDOW_OBJECT (gdk_window);
impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);
if (!impl->needs_display_region)
impl->needs_display_region = gdk_region_rectangle (&r);
else
gdk_region_union_with_rect (impl->needs_display_region, &r);
#if 0
gint nrects;
GdkRectangle* rects;
gdk_region_get_rectangles (impl->needs_display_region, &rects, &nrects);
printf ("SND %p INVALID with %d rects\n", impl, nrects);
for (gint n = 0; n < nrects; ++n) {
printf ("\t%d,%d %d x %d\n", rects[n].x, rects[n].y, rects[n].width, rects[n].height);
}
#endif
[super setNeedsDisplay:yn];
}
- (void) setNeedsDisplayInRect:(NSRect)rect
{
GdkWindowObject *private;
GdkWindowImplQuartz *impl;
GdkRectangle r = { rect.origin.x, rect.origin.y, rect.size.width, rect.size.height };
if (r.width >= 2147483647 || r.height >= 2147483647) {
NSRect bounds = [self bounds];
r.x = 0;
r.y = 0;
r.width = bounds.size.width;
r.height = bounds.size.height;
}
private = GDK_WINDOW_OBJECT (gdk_window);
impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);
if (!impl->needs_display_region)
impl->needs_display_region = gdk_region_rectangle (&r);
else
gdk_region_union_with_rect (impl->needs_display_region, &r);
#if 0
gint nrects;
GdkRectangle* rects;
gdk_region_get_rectangles (impl->needs_display_region, &rects, &nrects);
printf ("SNDiR %p INVALID with %d rects\n", impl, nrects);
for (gint n = 0; n < nrects; ++n) {
printf ("\t%d,%d %d x %d\n", rects[n].x, rects[n].y, rects[n].width, rects[n].height);
}
#endif
[super setNeedsDisplayInRect:rect];
}
-(void)drawRect: (NSRect)rect
{
GdkRectangle gdk_rect;
@ -647,31 +713,44 @@
return;
}
/* Clear our own bookkeeping of regions that need display */
if (impl->needs_display_region)
{
gdk_region_destroy (impl->needs_display_region);
impl->needs_display_region = NULL;
if (!impl->needs_display_region) {
[self getRectsBeingDrawn: &drawn_rects count: &count];
region = gdk_region_new ();
for (i = 0; i < count; i++)
{
gdk_rect.x = drawn_rects[i].origin.x;
gdk_rect.y = drawn_rects[i].origin.y;
gdk_rect.width = drawn_rects[i].size.width;
gdk_rect.height = drawn_rects[i].size.height;
gdk_region_union_with_rect (region, &gdk_rect);
}
} else {
region = impl->needs_display_region;
}
[self getRectsBeingDrawn: &drawn_rects count: &count];
region = gdk_region_new ();
for (i = 0; i < count; i++)
{
gdk_rect.x = drawn_rects[i].origin.x;
gdk_rect.y = drawn_rects[i].origin.y;
gdk_rect.width = drawn_rects[i].size.width;
gdk_rect.height = drawn_rects[i].size.height;
gdk_region_union_with_rect (region, &gdk_rect);
}
#if 0
gint nrects;
GdkRectangle* rects;
gdk_region_get_rectangles (region, &rects, &nrects);
printf ("%p drawRect with %d rects\n", impl, nrects);
for (gint n = 0; n < nrects; ++n) {
printf ("\t%d,%d %d x %d\n", rects[n].x, rects[n].y, rects[n].width, rects[n].height);
}
#endif
impl->in_paint_rect_count++;
/* this essentially generates an expose event */
_gdk_window_process_updates_recurse (gdk_window, region);
impl->in_paint_rect_count--;
if (impl->needs_display_region)
{
impl->needs_display_region = NULL;
}
gdk_region_destroy (region);
if (needsInvalidateShadow)
@ -681,6 +760,46 @@
}
}
- (void) reshape
{
GdkWindowObject *private;
GdkWindowImplQuartz *impl;
GdkRectangle r;
[super reshape];
NSRect bounds = [self bounds];
private = GDK_WINDOW_OBJECT (gdk_window);
impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);
if (impl->needs_display_region) {
gdk_region_destroy (impl->needs_display_region);
}
r.x = 0;
r.y = 0;
r.width = bounds.size.width;
r.height = bounds.size.height;
if (impl->needs_display_region)
gdk_region_destroy (impl->needs_display_region);
impl->needs_display_region = gdk_region_rectangle (&r);
#if 0
gint nrects;
GdkRectangle* rects;
gdk_region_get_rectangles (impl->needs_display_region, &rects, &nrects);
printf ("RS %p INVALID with %d rects\n", impl, nrects);
for (gint n = 0; n < nrects; ++n) {
printf ("\t%d,%d %d x %d\n", rects[n].x, rects[n].y, rects[n].width, rects[n].height);
}
#endif
}
-(void)setNeedsInvalidateShadow: (BOOL)invalidate
{
needsInvalidateShadow = invalidate;

View File

@ -282,11 +282,6 @@ _gdk_quartz_window_set_needs_display_in_rect (GdkWindow *window,
private = GDK_WINDOW_OBJECT (window);
impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);
if (!impl->needs_display_region)
impl->needs_display_region = gdk_region_new ();
gdk_region_union_with_rect (impl->needs_display_region, rect);
[impl->view setNeedsDisplayInRect:NSMakeRect (rect->x, rect->y,
rect->width, rect->height)];
@ -304,11 +299,6 @@ _gdk_quartz_window_set_needs_display_in_region (GdkWindow *window,
private = GDK_WINDOW_OBJECT (window);
impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);
if (!impl->needs_display_region)
impl->needs_display_region = gdk_region_new ();
gdk_region_union (impl->needs_display_region, region);
gdk_region_get_rectangles (region, &rects, &n_rects);
for (i = 0; i < n_rects; i++)