Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Williams2018-03-28 19:55:38 +0000
committerEric Williams2018-04-16 19:27:06 +0000
commit7ac153d75bbe583d855662ea077c9ab158c18169 (patch)
treed5e38fb2fcb5f64690151bcf1dd70f85a5c39b4d
parent802688f6202feab16b46f81f6e6b893c37341a30 (diff)
downloadeclipse.platform.swt-7ac153d75bbe583d855662ea077c9ab158c18169.tar.gz
eclipse.platform.swt-7ac153d75bbe583d855662ea077c9ab158c18169.tar.xz
eclipse.platform.swt-7ac153d75bbe583d855662ea077c9ab158c18169.zip
Bug 529431: [GTK3.10+] Snippet294 fails to draw Region
Fix input handling for GTK3.10+ setRegion behaviour. This does a manual check to see if the incoming event coordinates are within the set region: if they are, ignore them. This prevents events like SWT.Selection, SWT.Mouse[Up/Down], etc. from triggering when they are spawned from within the set region. There are two main limitations though: 1) we cannot stop native GTK events as we are doing the region drawing on the SWT level. This means things like mouse enter/leave, button pre-light, etc. will still be visible. We block the selection/click events at the SWT level, but unfortunately there is no reliable way to block these on the GTK level. 2) we also can't block all selection events, as some widgets are selectable using the keyboard (which has no coordinates). This means we could potentially block out selection events that *should* be sent. Tested on GTK3.22, no AllNonBrowser JUnit test failures. Change-Id: Ifd8377feb7d71a366edcdf6083d2bc94ab779886 Signed-off-by: Eric Williams <ericwill@redhat.com>
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT PI/cairo/library/cairo.c20
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT PI/cairo/library/cairo_custom.h1
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT PI/cairo/library/cairo_stats.c1
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT PI/cairo/library/cairo_stats.h1
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT PI/cairo/org/eclipse/swt/internal/cairo/Cairo.java10
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Button.java1
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Control.java52
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Table.java4
-rw-r--r--bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tree.java4
-rw-r--r--tests/org.eclipse.swt.tests.gtk/ManualTests/org/eclipse/swt/tests/gtk/snippets/Bug529431_SetRegionTesting.java7
10 files changed, 97 insertions, 4 deletions
diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/library/cairo.c b/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/library/cairo.c
index f79d487dba..e8d3ce9784 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/library/cairo.c
+++ b/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/library/cairo.c
@@ -855,6 +855,26 @@ JNIEXPORT jintLong JNICALL Cairo_NATIVE(_1cairo_1reference)
}
#endif
+#ifndef NO__1cairo_1region_1contains_1point
+JNIEXPORT jboolean JNICALL Cairo_NATIVE(_1cairo_1region_1contains_1point)
+ (JNIEnv *env, jclass that, jintLong arg0, jint arg1, jint arg2)
+{
+ jboolean rc = 0;
+ Cairo_NATIVE_ENTER(env, that, _1cairo_1region_1contains_1point_FUNC);
+/*
+ rc = (jboolean)cairo_region_contains_point(arg0, arg1, arg2);
+*/
+ {
+ Cairo_LOAD_FUNCTION(fp, cairo_region_contains_point)
+ if (fp) {
+ rc = (jboolean)((jboolean (CALLING_CONVENTION*)(jintLong, jint, jint))fp)(arg0, arg1, arg2);
+ }
+ }
+ Cairo_NATIVE_EXIT(env, that, _1cairo_1region_1contains_1point_FUNC);
+ return rc;
+}
+#endif
+
#ifndef NO__1cairo_1region_1get_1rectangle
JNIEXPORT void JNICALL Cairo_NATIVE(_1cairo_1region_1get_1rectangle)
(JNIEnv *env, jclass that, jintLong arg0, jint arg1, jintLong arg2)
diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/library/cairo_custom.h b/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/library/cairo_custom.h
index a164b42b53..43082af563 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/library/cairo_custom.h
+++ b/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/library/cairo_custom.h
@@ -31,6 +31,7 @@
#define cairo_pdf_surface_set_size_LIB LIB_CAIRO
#define cairo_ps_surface_set_size_LIB LIB_CAIRO
#define cairo_region_num_rectangles_LIB LIB_CAIRO
+#define cairo_region_contains_point_LIB LIB_CAIRO
#define cairo_region_get_rectangle_LIB LIB_CAIRO
#ifdef CAIRO_HAS_XLIB_SURFACE
diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/library/cairo_stats.c b/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/library/cairo_stats.c
index efd463895f..0511e14bde 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/library/cairo_stats.c
+++ b/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/library/cairo_stats.c
@@ -96,6 +96,7 @@ char * Cairo_nativeFunctionNames[] = {
"_1cairo_1push_1group",
"_1cairo_1rectangle",
"_1cairo_1reference",
+ "_1cairo_1region_1contains_1point",
"_1cairo_1region_1get_1rectangle",
"_1cairo_1region_1num_1rectangles",
"_1cairo_1reset_1clip",
diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/library/cairo_stats.h b/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/library/cairo_stats.h
index 8bdc8adad5..2a055a030a 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/library/cairo_stats.h
+++ b/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/library/cairo_stats.h
@@ -106,6 +106,7 @@ typedef enum {
_1cairo_1push_1group_FUNC,
_1cairo_1rectangle_FUNC,
_1cairo_1reference_FUNC,
+ _1cairo_1region_1contains_1point_FUNC,
_1cairo_1region_1get_1rectangle_FUNC,
_1cairo_1region_1num_1rectangles_FUNC,
_1cairo_1reset_1clip_FUNC,
diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/org/eclipse/swt/internal/cairo/Cairo.java b/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/org/eclipse/swt/internal/cairo/Cairo.java
index 63df89f412..1194701344 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/org/eclipse/swt/internal/cairo/Cairo.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT PI/cairo/org/eclipse/swt/internal/cairo/Cairo.java
@@ -1248,6 +1248,16 @@ public static final int cairo_region_num_rectangles(long /*int*/ region) {
}
}
/** @method flags=dynamic */
+public static final native boolean _cairo_region_contains_point(long /*int*/ region, int x, int y);
+public static final boolean cairo_region_contains_point(long /*int*/ region, int x, int y) {
+ lock.lock();
+ try {
+ return _cairo_region_contains_point(region, x, y);
+ } finally {
+ lock.unlock();
+ }
+}
+/** @method flags=dynamic */
public static final native void _cairo_region_get_rectangle(long /*int*/ region, int nth, long /*int*/ rectangle);
public static final void cairo_region_get_rectangle(long /*int*/ region, int nth, long /*int*/ rectangle) {
lock.lock();
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Button.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Button.java
index cddd2817ef..ca8aa54ebb 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Button.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Button.java
@@ -504,6 +504,7 @@ long /*int*/ gtk_button_press_event (long /*int*/ widget, long /*int*/ event) {
@Override
long /*int*/ gtk_clicked (long /*int*/ widget) {
+ if (containedInRegion(lastInput.x, lastInput.y)) return 0;
if ((style & SWT.RADIO) != 0) {
if ((parent.getStyle () & SWT.NO_RADIO_GROUP) != 0) {
setSelection (!selected);
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Control.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Control.java
index 2b42fefd28..239c99b77f 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Control.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Control.java
@@ -59,12 +59,19 @@ public abstract class Control extends Widget implements Drawable {
Image backgroundImage;
Font font;
Region region;
+ long /*int*/ eventRegion;
String toolTipText;
Object layoutData;
Accessible accessible;
Control labelRelation;
String cssBackground, cssForeground = " ";
boolean drawRegion;
+ /**
+ * Point for storing the (x, y) coordinate of the last input (click/scroll/etc.).
+ * This is useful for checking input event coordinates in methods that act on input,
+ * but do not receive coordinates (like gtk_clicked, for example). See bug 529431.
+ */
+ Point lastInput = new Point(0, 0);
LinkedList <Event> dragDetectionQueue;
@@ -429,6 +436,7 @@ boolean hooksPaint () {
long /*int*/ hoverProc (long /*int*/ widget) {
int [] x = new int [1], y = new int [1], mask = new int [1];
gdk_window_get_device_position (0, x, y, mask);
+ if (containedInRegion(x[0], y[0])) return 0;
sendMouseEvent (SWT.MouseHover, 0, /*time*/0, x [0], y [0], false, mask [0]);
/* Always return zero in order to cancel the hover timer */
return 0;
@@ -713,6 +721,21 @@ void checkMirrored () {
if ((style & SWT.RIGHT_TO_LEFT) != 0) style |= SWT.MIRRORED;
}
+/**
+ * Convenience method for checking whether an (x, y) coordinate is in the set
+ * region. Only relevant for GTK3.10+.
+ *
+ * @param x an x coordinate
+ * @param y a y coordinate
+ * @return true if the coordinate (x, y) is in the region, false otherwise
+ */
+boolean containedInRegion (int x, int y) {
+ if (drawRegion && eventRegion != 0) {
+ return Cairo.cairo_region_contains_point(eventRegion, x, y);
+ }
+ return false;
+}
+
long /*int*/ childStyle () {
return parent.childStyle ();
}
@@ -3335,6 +3358,9 @@ long /*int*/ gtk_button_press_event (long /*int*/ widget, long /*int*/ event) {
long /*int*/ gtk_button_press_event (long /*int*/ widget, long /*int*/ event, boolean sendMouseDown) {
GdkEventButton gdkEvent = new GdkEventButton ();
OS.memmove (gdkEvent, event, GdkEventButton.sizeof);
+ lastInput.x = (int) gdkEvent.x;
+ lastInput.y = (int) gdkEvent.y;
+ if (containedInRegion(lastInput.x, lastInput.y)) return 0;
if (gdkEvent.type == GDK.GDK_3BUTTON_PRESS) return 0;
/*
@@ -3414,6 +3440,9 @@ long /*int*/ gtk_button_press_event (long /*int*/ widget, long /*int*/ event, bo
long /*int*/ gtk_button_release_event (long /*int*/ widget, long /*int*/ event) {
GdkEventButton gdkEvent = new GdkEventButton ();
OS.memmove (gdkEvent, event, GdkEventButton.sizeof);
+ lastInput.x = (int) gdkEvent.x;
+ lastInput.y = (int) gdkEvent.y;
+ if (containedInRegion(lastInput.x, lastInput.y)) return 0;
return sendMouseEvent (SWT.MouseUp, gdkEvent.button, display.clickCount, 0, false, gdkEvent.time, gdkEvent.x_root, gdkEvent.y_root, false, gdkEvent.state) ? 0 : 1;
}
@@ -3448,6 +3477,10 @@ long /*int*/ gtk_enter_notify_event (long /*int*/ widget, long /*int*/ event) {
if (display.currentControl == this) return 0;
GdkEventCrossing gdkEvent = new GdkEventCrossing ();
OS.memmove (gdkEvent, event, GdkEventCrossing.sizeof);
+ lastInput.x = (int) gdkEvent.x;
+ lastInput.y = (int) gdkEvent.y;
+ if (containedInRegion(lastInput.x, lastInput.y)) return 0;
+
/*
* It is possible to send out too many enter/exit events if entering a
* control through a subwindow. The fix is to return without sending any
@@ -3560,6 +3593,7 @@ void cairoClipRegion (long /*int*/ cairo) {
GDK.gdk_cairo_region(cairo, actualRegion);
Cairo.cairo_clip(cairo);
Cairo.cairo_paint(cairo);
+ eventRegion = actualRegion;
}
@Override
@@ -3702,11 +3736,14 @@ long /*int*/ gtk_key_release_event (long /*int*/ widget, long /*int*/ event) {
@Override
long /*int*/ gtk_leave_notify_event (long /*int*/ widget, long /*int*/ event) {
if (display.currentControl != this) return 0;
+ GdkEventCrossing gdkEvent = new GdkEventCrossing ();
+ OS.memmove (gdkEvent, event, GdkEventCrossing.sizeof);
+ lastInput.x = (int) gdkEvent.x;
+ lastInput.y = (int) gdkEvent.y;
+ if (containedInRegion(lastInput.x, lastInput.y)) return 0;
display.removeMouseHoverTimeout (handle);
int result = 0;
if (sendLeaveNotify () || display.getCursorControl () == null) {
- GdkEventCrossing gdkEvent = new GdkEventCrossing ();
- OS.memmove (gdkEvent, event, GdkEventCrossing.sizeof);
if (gdkEvent.mode != GDK.GDK_CROSSING_NORMAL && gdkEvent.mode != GDK.GDK_CROSSING_UNGRAB) return 0;
if ((gdkEvent.state & (GDK.GDK_BUTTON1_MASK | GDK.GDK_BUTTON2_MASK | GDK.GDK_BUTTON3_MASK)) != 0) return 0;
result = sendMouseEvent (SWT.MouseExit, 0, gdkEvent.time, gdkEvent.x_root, gdkEvent.y_root, false, gdkEvent.state) ? 0 : 1;
@@ -3742,6 +3779,9 @@ long /*int*/ gtk_motion_notify_event (long /*int*/ widget, long /*int*/ event) {
int result;
GdkEventMotion gdkEvent = new GdkEventMotion ();
OS.memmove (gdkEvent, event, GdkEventMotion.sizeof);
+ lastInput.x = (int) gdkEvent.x;
+ lastInput.y = (int) gdkEvent.y;
+ if (containedInRegion(lastInput.x, lastInput.y)) return 0;
/*
* Feature in GTK: DND detection for X.11 & Wayland support is done through motion notify event
* instead of mouse click event. See Bug 503431.
@@ -3827,6 +3867,9 @@ long /*int*/ gtk_realize (long /*int*/ widget) {
long /*int*/ gtk_scroll_event (long /*int*/ widget, long /*int*/ eventPtr) {
GdkEventScroll gdkEvent = new GdkEventScroll ();
OS.memmove (gdkEvent, eventPtr, GdkEventScroll.sizeof);
+ lastInput.x = (int) gdkEvent.x;
+ lastInput.y = (int) gdkEvent.y;
+ if (containedInRegion(lastInput.x, lastInput.y)) return 0;
switch (gdkEvent.direction) {
case GDK.GDK_SCROLL_UP:
return sendMouseEvent (SWT.MouseWheel, 0, 3, SWT.SCROLL_LINE, true, gdkEvent.time, gdkEvent.x_root, gdkEvent.y_root, false, gdkEvent.state) ? 0 : 1;
@@ -4340,6 +4383,7 @@ void sendFocusEvent (int type) {
}
boolean sendGestureEvent (int stateMask, int detail, int x, int y, double delta) {
+ if (containedInRegion(x, y)) return false;
switch (detail) {
case SWT.GESTURE_ROTATE: {
return sendGestureEvent(stateMask, detail, x, y, delta, 0, 0, 0);
@@ -4360,12 +4404,14 @@ void sendFocusEvent (int type) {
}
boolean sendGestureEvent (int stateMask, int detail, int x, int y, double xDirection, double yDirection) {
+ if (containedInRegion(x, y)) return false;
if (detail == SWT.GESTURE_SWIPE) {
return sendGestureEvent(stateMask, detail, x, y, 0, (int)xDirection, (int)yDirection, 0);
} else return false;
}
boolean sendGestureEvent (int stateMask, int detail, int x, int y, double rotation, int xDirection, int yDirection, double magnification) {
+ if (containedInRegion(x, y)) return false;
Event event = new Event ();
event.stateMask = stateMask;
event.detail = detail;
@@ -4412,6 +4458,7 @@ boolean sendLeaveNotify() {
}
boolean sendMouseEvent (int type, int button, int time, double x, double y, boolean is_hint, int state) {
+ if (containedInRegion((int) x, (int) y)) return true;
return sendMouseEvent (type, button, 0, 0, false, time, x, y, is_hint, state);
}
@@ -4421,6 +4468,7 @@ boolean sendMouseEvent (int type, int button, int time, double x, double y, bool
* false - event sending canceled by user.
*/
boolean sendMouseEvent (int type, int button, int count, int detail, boolean send, int time, double x, double y, boolean is_hint, int state) {
+ if (containedInRegion((int) x, (int) y)) return true;
if (!hooks (type) && !filters (type)) {
/*
* On Wayland, MouseDown events are cached for DnD purposes, but
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Table.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Table.java
index 9e218f4b92..7aabcef410 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Table.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Table.java
@@ -2184,6 +2184,10 @@ void sendTreeDefaultSelection() {
long /*int*/ gtk_button_release_event (long /*int*/ widget, long /*int*/ event) {
GdkEventButton gdkEvent = new GdkEventButton ();
OS.memmove (gdkEvent, event, GdkEventButton.sizeof);
+ // Check region since super.gtk_button_release_event() isn't called
+ lastInput.x = (int) gdkEvent.x;
+ lastInput.y = (int) gdkEvent.y;
+ if (containedInRegion(lastInput.x, lastInput.y)) return 0;
if (gdkEvent.window != GTK.gtk_tree_view_get_bin_window (handle)) return 0;
/*
* Feature in GTK. In multi-select tree view there is a problem with using DnD operations while also selecting multiple items.
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tree.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tree.java
index b26870ed8a..98be900516 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tree.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tree.java
@@ -2205,6 +2205,10 @@ void sendTreeDefaultSelection() {
long /*int*/ gtk_button_release_event (long /*int*/ widget, long /*int*/ event) {
GdkEventButton gdkEvent = new GdkEventButton ();
OS.memmove (gdkEvent, event, GdkEventButton.sizeof);
+ // Check region since super.gtk_button_release_event() isn't called
+ lastInput.x = (int) gdkEvent.x;
+ lastInput.y = (int) gdkEvent.y;
+ if (containedInRegion(lastInput.x, lastInput.y)) return 0;
if (gdkEvent.window != GTK.gtk_tree_view_get_bin_window (handle)) return 0;
/*
* Feature in GTK. In multi-select tree view there is a problem with using DnD operations while also selecting multiple items.
diff --git a/tests/org.eclipse.swt.tests.gtk/ManualTests/org/eclipse/swt/tests/gtk/snippets/Bug529431_SetRegionTesting.java b/tests/org.eclipse.swt.tests.gtk/ManualTests/org/eclipse/swt/tests/gtk/snippets/Bug529431_SetRegionTesting.java
index 135afdc265..fab5f9bdf8 100644
--- a/tests/org.eclipse.swt.tests.gtk/ManualTests/org/eclipse/swt/tests/gtk/snippets/Bug529431_SetRegionTesting.java
+++ b/tests/org.eclipse.swt.tests.gtk/ManualTests/org/eclipse/swt/tests/gtk/snippets/Bug529431_SetRegionTesting.java
@@ -86,8 +86,11 @@ public class Bug529431_SetRegionTesting {
// define the shape of the button using setRegion
widget.setRegion(region);
- // Uncomment to test input handling
-// b2.addListener(SWT.Selection, e -> shell.close());
+ // test input handling
+ widget.addListener(SWT.Selection, e -> System.out.println("SWT.Selection sent"));
+ widget.addListener(SWT.MouseDown, e -> System.out.println("SWT.MouseDown sent"));
+ widget.addListener(SWT.MouseExit, e -> System.out.println("SWT.MouseExit sent"));
+ widget.addListener(SWT.MouseEnter, e -> System.out.println("SWT.MouseEnter sent"));
shell.setSize(200,200);
shell.open();

Back to the top