diff options
6 files changed, 227 insertions, 38 deletions
diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os.c b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os.c index 16ae5c75c4..8e32951ff9 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os.c +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os.c @@ -2747,6 +2747,26 @@ JNIEXPORT jint JNICALL GDK_NATIVE(_1gdk_1visual_1get_1depth) } #endif +#ifndef NO__1gdk_1window_1begin_1draw_1frame +JNIEXPORT jlong JNICALL GDK_NATIVE(_1gdk_1window_1begin_1draw_1frame) + (JNIEnv *env, jclass that, jlong arg0, jlong arg1) +{ + jlong rc = 0; + GDK_NATIVE_ENTER(env, that, _1gdk_1window_1begin_1draw_1frame_FUNC); +/* + rc = (jlong)gdk_window_begin_draw_frame((GdkWindow *)arg0, (cairo_region_t *)arg1); +*/ + { + GDK_LOAD_FUNCTION(fp, gdk_window_begin_draw_frame) + if (fp) { + rc = (jlong)((jlong (CALLING_CONVENTION*)(GdkWindow *, cairo_region_t *))fp)((GdkWindow *)arg0, (cairo_region_t *)arg1); + } + } + GDK_NATIVE_EXIT(env, that, _1gdk_1window_1begin_1draw_1frame_FUNC); + return rc; +} +#endif + #ifndef NO__1gdk_1window_1create_1similar_1surface JNIEXPORT jlong JNICALL GDK_NATIVE(_1gdk_1window_1create_1similar_1surface) (JNIEnv *env, jclass that, jlong arg0, jint arg1, jint arg2, jint arg3) @@ -2769,6 +2789,26 @@ JNIEXPORT void JNICALL GDK_NATIVE(_1gdk_1window_1destroy) } #endif +#ifndef NO__1gdk_1window_1end_1draw_1frame +JNIEXPORT jlong JNICALL GDK_NATIVE(_1gdk_1window_1end_1draw_1frame) + (JNIEnv *env, jclass that, jlong arg0, jlong arg1) +{ + jlong rc = 0; + GDK_NATIVE_ENTER(env, that, _1gdk_1window_1end_1draw_1frame_FUNC); +/* + rc = (jlong)gdk_window_end_draw_frame((GdkWindow *)arg0, (GdkDrawingContext *)arg1); +*/ + { + GDK_LOAD_FUNCTION(fp, gdk_window_end_draw_frame) + if (fp) { + rc = (jlong)((jlong (CALLING_CONVENTION*)(GdkWindow *, GdkDrawingContext *))fp)((GdkWindow *)arg0, (GdkDrawingContext *)arg1); + } + } + GDK_NATIVE_EXIT(env, that, _1gdk_1window_1end_1draw_1frame_FUNC); + return rc; +} +#endif + #ifndef NO__1gdk_1window_1focus JNIEXPORT void JNICALL GDK_NATIVE(_1gdk_1window_1focus) (JNIEnv *env, jclass that, jlong arg0, jint arg1) diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.c b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.c index 40806b67d5..b0eac77a80 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.c +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.c @@ -212,8 +212,10 @@ char * GDK_nativeFunctionNames[] = { "_1gdk_1unicode_1to_1keyval", "_1gdk_1utf8_1to_1string_1target", "_1gdk_1visual_1get_1depth", + "_1gdk_1window_1begin_1draw_1frame", "_1gdk_1window_1create_1similar_1surface", "_1gdk_1window_1destroy", + "_1gdk_1window_1end_1draw_1frame", "_1gdk_1window_1focus", "_1gdk_1window_1get_1children", "_1gdk_1window_1get_1device_1position", diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.h b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.h index 935e7d2522..5145d14c27 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.h +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/os_stats.h @@ -222,8 +222,10 @@ typedef enum { _1gdk_1unicode_1to_1keyval_FUNC, _1gdk_1utf8_1to_1string_1target_FUNC, _1gdk_1visual_1get_1depth_FUNC, + _1gdk_1window_1begin_1draw_1frame_FUNC, _1gdk_1window_1create_1similar_1surface_FUNC, _1gdk_1window_1destroy_FUNC, + _1gdk_1window_1end_1draw_1frame_FUNC, _1gdk_1window_1focus_FUNC, _1gdk_1window_1get_1children_FUNC, _1gdk_1window_1get_1device_1position_FUNC, diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/GDK.java b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/GDK.java index 62f8fc2ff8..6132c5c132 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/GDK.java +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/GDK.java @@ -524,6 +524,34 @@ public class GDK extends OS { } /** * @param window cast=(GdkWindow *) + * @param region cast=(cairo_region_t *) + * @method flags=dynamic + */ + public static final native long _gdk_window_begin_draw_frame(long window, long region); + public static final long gdk_window_begin_draw_frame(long window, long region) { + lock.lock(); + try { + return _gdk_window_begin_draw_frame(window, region); + } finally { + lock.unlock(); + } + } + /** + * @param window cast=(GdkWindow *) + * @param context cast=(GdkDrawingContext *) + * @method flags=dynamic + */ + public static final native long _gdk_window_end_draw_frame(long window, long context); + public static final long gdk_window_end_draw_frame(long window, long context) { + lock.lock(); + try { + return _gdk_window_end_draw_frame(window, context); + } finally { + lock.unlock(); + } + } + /** + * @param window cast=(GdkWindow *) */ public static final native int _gdk_window_get_state(long window); /** [GTK3 only, if-def'd in os.h] */ diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Canvas.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Canvas.java index fc6e3e411b..81e68c30d5 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Canvas.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Canvas.java @@ -45,7 +45,6 @@ import org.eclipse.swt.internal.gtk.*; public class Canvas extends Composite { Caret caret; IME ime; - long cachedCairo; boolean blink, drawFlag; Canvas () {} @@ -179,7 +178,6 @@ long gtk_draw (long widget, long cairo) { result = super.gtk_draw (widget, cairo); if (isFocus) caret.setFocus (); } else { - this.cachedCairo = cairo; result = super.gtk_draw (widget, cairo); /* * blink is needed to be checked as gtk_draw() signals sent from other parts of the canvas @@ -313,14 +311,19 @@ void reskinChildren (int flags) { public void scroll (int destX, int destY, int x, int y, int width, int height, boolean all) { checkWidget(); if (width <= 0 || height <= 0) return; + /* + * scrollInPixels() doesn't seem to be needed on GTK4, so we can return early. + * In fact it doesn't seem to be needed on GTK3 either, but it's been left + * here for stability on older GTK3 versions. The investigation + * as to why it's unneeded is left as a TODO. See bug 546274. + */ + if (GTK.GTK4) return; Point destination = DPIUtil.autoScaleUp (new Point (destX, destY)); Rectangle srcRect = DPIUtil.autoScaleUp (new Rectangle (x, y, width, height)); scrollInPixels(destination.x, destination.y, srcRect.x, srcRect.y, srcRect.width, srcRect.height, all); } void scrollInPixels (int destX, int destY, int x, int y, int width, int height, boolean all) { - long cairo = this.cachedCairo; - if (cairo == 0) return; if ((style & SWT.MIRRORED) != 0) { int clientWidth = getClientWidth (); x = clientWidth - width - x; @@ -331,12 +334,9 @@ void scrollInPixels (int destX, int destY, int x, int y, int width, int height, if (!isVisible ()) return; boolean isFocus = caret != null && caret.isFocusCaret (); if (isFocus) caret.killFocus (); - GdkRectangle clipRect = new GdkRectangle (); - GDK.gdk_cairo_get_clip_rectangle (cairo, clipRect); + long window = paintWindow (); + long visibleRegion = GDK.gdk_window_get_visible_region (window); cairo_rectangle_int_t srcRect = new cairo_rectangle_int_t (); - srcRect.convertFromGdkRectangle(clipRect); - long gdkResource = GTK.GTK4? paintSurface () : paintWindow (); - long visibleRegion = Cairo.cairo_region_create_rectangle(srcRect); srcRect.x = x; srcRect.y = y; /* @@ -384,48 +384,63 @@ void scrollInPixels (int destX, int destY, int x, int y, int width, int height, redrawWidget (x, y, width, height, false, false, false); redrawWidget (destX, destY, width, height, false, false, false); } else { - Cairo.cairo_push_group(cairo); - Cairo.cairo_paint(cairo); - Cairo.cairo_pop_group_to_source(cairo); + long cairo = 0; + long context = 0; + if (GTK.GTK_VERSION >= OS.VERSION(3, 22, 0)) { + long cairo_region = GDK.gdk_window_get_visible_region(window); + context = GDK.gdk_window_begin_draw_frame(window, cairo_region); + cairo = GDK.gdk_drawing_context_get_cairo_context(context); + } else { + cairo = GDK.gdk_cairo_create(window); + } + if (Cairo.cairo_version() < Cairo.CAIRO_VERSION_ENCODE(1, 12, 0)) { + GDK.gdk_cairo_set_source_window(cairo, window, 0, 0); + } else { + Cairo.cairo_push_group(cairo); + GDK.gdk_cairo_set_source_window(cairo, window, 0, 0); + Cairo.cairo_paint(cairo); + Cairo.cairo_pop_group_to_source(cairo); + } double[] matrix = {1, 0, 0, 1, -deltaX, -deltaY}; Cairo.cairo_pattern_set_matrix(Cairo.cairo_get_source(cairo), matrix); Cairo.cairo_rectangle(cairo, copyRect.x + deltaX, copyRect.y + deltaY, copyRect.width, copyRect.height); Cairo.cairo_clip(cairo); Cairo.cairo_paint(cairo); + if (GTK.GTK_VERSION >= OS.VERSION(3, 22, 0)) { + if (context != 0) GDK.gdk_window_end_draw_frame(window, context); + } else { + Cairo.cairo_destroy(cairo); + } boolean disjoint = (destX + width < x) || (x + width < destX) || (destY + height < y) || (y + height < destY); if (disjoint) { - cairo_rectangle_int_t cairoRect = new cairo_rectangle_int_t(); - cairoRect.x = x; - cairoRect.y = y; - cairoRect.width = width; - cairoRect.height = height; - Cairo.cairo_region_union_rectangle (invalidateRegion, cairoRect); + cairo_rectangle_int_t rect = new cairo_rectangle_int_t(); + rect.x = x; + rect.y = y; + rect.width = width; + rect.height = height; + Cairo.cairo_region_union_rectangle (invalidateRegion, rect); } else { - cairo_rectangle_int_t cairoRect = new cairo_rectangle_int_t(); + cairo_rectangle_int_t rect = new cairo_rectangle_int_t(); if (deltaX != 0) { int newX = destX - deltaX; if (deltaX < 0) newX = destX + width; - cairoRect.x = newX; - cairoRect.y = y; - cairoRect.width = Math.abs(deltaX); - cairoRect.height = height; - Cairo.cairo_region_union_rectangle (invalidateRegion, cairoRect); + rect.x = newX; + rect.y = y; + rect.width = Math.abs(deltaX); + rect.height = height; + Cairo.cairo_region_union_rectangle (invalidateRegion, rect); } if (deltaY != 0) { int newY = destY - deltaY; if (deltaY < 0) newY = destY + height; - cairoRect.x = x; - cairoRect.y = newY; - cairoRect.width = width; - cairoRect.height = Math.abs(deltaY); - Cairo.cairo_region_union_rectangle (invalidateRegion, cairoRect); + rect.x = x; + rect.y = newY; + rect.width = width; + rect.height = Math.abs(deltaY); + Cairo.cairo_region_union_rectangle (invalidateRegion, rect); } } - if (GTK.GTK4) { - GDK.gdk_surface_invalidate_region(gdkResource, invalidateRegion); - } else { - GDK.gdk_window_invalidate_region(gdkResource, invalidateRegion, all); - } + GDK.gdk_window_invalidate_region(window, invalidateRegion, all); } Cairo.cairo_region_destroy (visibleRegion); Cairo.cairo_region_destroy (copyRegion); @@ -434,10 +449,10 @@ void scrollInPixels (int destX, int destY, int x, int y, int width, int height, Control [] children = _getChildren (); for (int i=0; i<children.length; i++) { Control child = children [i]; - Rectangle childBounds = child.getBoundsInPixels (); - if (Math.min(x + width, childBounds.x + childBounds.width) >= Math.max (x, childBounds.x) && - Math.min(y + height, childBounds.y + childBounds.height) >= Math.max (y, childBounds.y)) { - child.setLocationInPixels (childBounds.x + deltaX, childBounds.y + deltaY); + Rectangle rect = child.getBoundsInPixels (); + if (Math.min(x + width, rect.x + rect.width) >= Math.max (x, rect.x) && + Math.min(y + height, rect.y + rect.height) >= Math.max (y, rect.y)) { + child.setLocationInPixels (rect.x + deltaX, rect.y + deltaY); } } } diff --git a/tests/org.eclipse.swt.tests.gtk/ManualTests/org/eclipse/swt/tests/gtk/snippets/Bug546274_StyledTextVMCrash.java b/tests/org.eclipse.swt.tests.gtk/ManualTests/org/eclipse/swt/tests/gtk/snippets/Bug546274_StyledTextVMCrash.java new file mode 100644 index 0000000000..878da10e23 --- /dev/null +++ b/tests/org.eclipse.swt.tests.gtk/ManualTests/org/eclipse/swt/tests/gtk/snippets/Bug546274_StyledTextVMCrash.java @@ -0,0 +1,102 @@ +/******************************************************************************* + * Copyright (c) 2019 Syntevo and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Syntevo - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.tests.gtk.snippets; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +public class Bug546274_StyledTextVMCrash { + private static class MyStyledText extends StyledText { + int cairoCacheSize = 16; // see MAX_FREED_POOL_SIZE in cairo + Image tempImages[] = new Image[cairoCacheSize]; + GC tempGCs[] = new GC[cairoCacheSize]; + + public MyStyledText(Composite parent, int style) { + super(parent, style); + allocTempGCs(); + } + + void allocTempGCs() { + for (int i = 0; i < cairoCacheSize; i++) { + tempImages[i] = new Image(this.getDisplay(), 1, 1); + tempGCs[i] = new GC(tempImages[i]); + } + } + + @Override + public void scroll(int destX, int destY, int x, int y, int width, int height, boolean all) { + // Evaluate with debugger: (cachedCairo.handle == this.cachedCairo); + // This already shows that 'this.cachedCairo' can't be used. + Image tempImage = new Image(this.getDisplay(), 1, 1); + GC cachedCairo = new GC(tempImage); + + // Evict 'cachedCairo' from Cairo's cache 'context_pool' by + // 1) Populating cache with free objects + // 2) Releasing 'cachedCairo' and it will be truly deleted + { + if (tempGCs != null) { + for (int i = 0; i < tempGCs.length; i++) { + tempGCs[i].dispose(); + tempImages[i].dispose(); + } + } + + cachedCairo.dispose(); + tempImage.dispose(); + } + + // Make sure the same malloc() block can't be used again + for (int i = 1; i < 1024; i++) { + // Intentional leak + org.eclipse.swt.internal.gtk.GTK.malloc(i); + } + + super.scroll(destX, destY, x, y, width, height, all); + + // Drain Cairo's cache 'context_pool' to force the next cairo to use malloc() + allocTempGCs(); + } + } + + public static void main(String[] args) { + final Display display = new Display(); + + final Shell shell = new Shell(display, SWT.SHELL_TRIM); + shell.setLayout(new FillLayout(SWT.VERTICAL)); + shell.setSize(300, 200); + + String hintText = "Try to horz scroll this StyledText\n" + + "\n" + + "Horz scrollbar is forced by this long line of text xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; + + final StyledText text = new MyStyledText(shell, SWT.BORDER | SWT.H_SCROLL); + text.setText(hintText); + + shell.open(); + + while (!shell.isDisposed()) { + if (!display.readAndDispatch()) { + display.sleep(); + } + } + + display.dispose(); + } +} |