diff options
3 files changed, 130 insertions, 0 deletions
diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/OS.java b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/OS.java index 813c4b0e64..8715917033 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/OS.java +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/OS.java @@ -717,6 +717,11 @@ public class OS extends C { */ public static final String GTK_THEME_SET_NAME; /** + * True iff overlay scrolling has been disabled via GTK_OVERLAY_SCROLLING=0. + * See bug 546248. + */ + public static final boolean GTK_OVERLAY_SCROLLING_DISABLED; + /** * True if SWT is running on the GNOME desktop environment. */ public static final boolean isGNOME; @@ -769,6 +774,14 @@ public class OS extends C { GTK_THEME_SET = gtkThemeSet; GTK_THEME_SET_NAME = gtkThemeName; + String scrollingProperty = "GTK_OVERLAY_SCROLLING"; + String scrollingCheck = getEnvironmentalVariable(scrollingProperty); + boolean scrollingDisabled = false; + if (scrollingCheck != null && scrollingCheck.equals("0")) { + scrollingDisabled = true; + } + GTK_OVERLAY_SCROLLING_DISABLED = scrollingDisabled; + Map<String, String> env = System.getenv(); String desktopEnvironment = env.get("XDG_CURRENT_DESKTOP"); boolean gnomeDetected = false; diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Scrollable.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Scrollable.java index d4a8252ace..2344fbe300 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Scrollable.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Scrollable.java @@ -304,6 +304,45 @@ public ScrollBar getVerticalBar () { } @Override +long gtk_draw (long widget, long cairo) { + /* + * Draw events destined for an SwtFixed instance will sometimes + * only be redrawing the scrollbars attached to it. GTK will send many + * draw events to an SwtFixed instance if: + * 1) that instance has overlay scrollbars attached to it, and + * 2) the mouse has just left (leave-notify) that SwtFixed widget. + * + * Such extra draw events cause extra SWT.Paint events to be sent and + * reduce performance. The fix is to check if the dirty region in need + * of a redraw is the same region that the scroll bars occupy, and ignore + * draw events that target such cases. See bug 546248. + */ + boolean overlayScrolling = !OS.GTK_OVERLAY_SCROLLING_DISABLED && + GTK.GTK_VERSION >= OS.VERSION(3, 16, 0); + if (overlayScrolling && OS.G_OBJECT_TYPE(widget) == OS.swt_fixed_get_type()) { + if ((style & SWT.V_SCROLL) != 0 && verticalBar != null) { + GtkAllocation verticalBarAlloc = new GtkAllocation(); + GTK.gtk_widget_get_allocation(verticalBar.handle, verticalBarAlloc); + GdkRectangle rect = new GdkRectangle(); + GDK.gdk_cairo_get_clip_rectangle(cairo, rect); + if (rect.width == verticalBarAlloc.width && rect.height == verticalBarAlloc.height) { + return 0; + } + } + if ((style & SWT.H_SCROLL) != 0 && horizontalBar != null) { + GtkAllocation horizontalBarAlloc = new GtkAllocation(); + GTK.gtk_widget_get_allocation(horizontalBar.handle, horizontalBarAlloc); + GdkRectangle rect = new GdkRectangle(); + GDK.gdk_cairo_get_clip_rectangle(cairo, rect); + if (rect.width == horizontalBarAlloc.width && rect.height == horizontalBarAlloc.height) { + return 0; + } + } + } + return super.gtk_draw(widget, cairo); +} + +@Override long gtk_scroll_event (long widget, long eventPtr) { long result = super.gtk_scroll_event (widget, eventPtr); diff --git a/tests/org.eclipse.swt.tests.gtk/ManualTests/org/eclipse/swt/tests/gtk/snippets/Bug546248_CompositeTooManyPaintEvents.java b/tests/org.eclipse.swt.tests.gtk/ManualTests/org/eclipse/swt/tests/gtk/snippets/Bug546248_CompositeTooManyPaintEvents.java new file mode 100644 index 0000000000..c4c7f23190 --- /dev/null +++ b/tests/org.eclipse.swt.tests.gtk/ManualTests/org/eclipse/swt/tests/gtk/snippets/Bug546248_CompositeTooManyPaintEvents.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2019 Thomas Singer 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: + * Thomas Singer - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.tests.gtk.snippets; + +import java.text.DateFormat; +import java.util.Date; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Shell; + +public class Bug546248_CompositeTooManyPaintEvents { + + public static void main(String[] args) { + final Display display = new Display(); + + final StringBuilder buffer = new StringBuilder(); + + final int period = 250; + display.timerExec(period, new Runnable() { + @Override + public void run() { + if (buffer.length() > 0) { + System.out.println(DateFormat.getTimeInstance().format(new Date()) + ": " + buffer); + buffer.setLength(0); + } + if (!display.isDisposed()) { + display.timerExec(period, this); + } + } + }); + + final Shell shell = new Shell(display); + shell.setLayout(new FillLayout()); + + final Composite composite = new Composite(shell, SWT.DOUBLE_BUFFERED | SWT.V_SCROLL); + final Listener listener = event -> { + if (event.type == SWT.Paint) { + buffer.append('p'); + event.gc.drawText("Hello world", 0, 0); + } + else if (event.type == SWT.MouseMove) { + buffer.append('m'); + } + else if (event.type == SWT.MouseHover) { + buffer.append('h'); + } + }; + composite.addListener(SWT.Paint, listener); + composite.addListener(SWT.MouseMove, listener); + composite.addListener(SWT.MouseHover, listener); + + shell.setSize(400, 300); + shell.open(); + + while (!shell.isDisposed()) { + if (!display.readAndDispatch()) { + display.sleep(); + } + } + + display.dispose(); + } +} |