Skip to content

Commit 1bb1016

Browse files
authored
Issue #247 - [GTK][Wayland] Drag&drop gets stuck (#248)
The problem was introduced in patch for Bug 541635, which accidentally ignored calculated `dragging` variable, causing drag to begin immediately when mouse is moved. Also, it introduced a crazy loop, which, if it proceeds to second iteration, will loop forever, because GTK events can't be processed when main thread is busy running the infinite loop. Luckily, it seems that the loop always exited on first iteration, and its meaning was simply to `return false` when mouse is not down. The reason why drag&drop can become stuck is not exactly clear, but I'm confident that it was related to very short drag&drop mouse move threshold (basically, 1px). With the regular threshold, the bug may still be present, but it's almost impossible to reproduce. The real case behind this bug is that when actual human double-clicks a TreeItem, sometimes mouse will be moved very slightly when doing so, which started a drag&drop, which got stuck and caused further problems in applications which were puzzled by never ending drag&drop. Also, it results in `TreeDragSourceEffect` image leak, because it never receives the drag end event. Signed-off-by: Alexandr Miloslavskiy <[email protected]>
1 parent 34a9c4c commit 1bb1016

File tree

2 files changed

+97
-27
lines changed

2 files changed

+97
-27
lines changed

bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Control.java

Lines changed: 17 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,6 @@ public abstract class Control extends Widget implements Drawable {
113113
* is currently pressed or released for DND.
114114
*/
115115
static boolean mouseDown;
116-
boolean dragBegun;
117116

118117
/**
119118
* Flag to check the scale factor upon the first drawing of this Control.
@@ -2671,36 +2670,36 @@ boolean dragDetect (int button, int count, int stateMask, int x, int y) {
26712670
}
26722671

26732672
boolean dragDetect (int x, int y, boolean filter, boolean dragOnTimeout, boolean [] consume) {
2674-
boolean dragging = false;
26752673
/*
26762674
* Feature in GTK: In order to support both X.11/Wayland, GTKGestures are used
26772675
* as of GTK3.14 in order to acquire mouse position offsets to decide on dragging.
26782676
* See Bug 503431.
26792677
*/
26802678
if (!OS.isX11()) { // Wayland
2679+
// Don't drag if mouse is not down. This condition is not as
2680+
// trivial as it seems, see Bug 541635 where drag is tested
2681+
// after drag already completed and mouse is released.
2682+
if (!mouseDown) {
2683+
return false;
2684+
}
2685+
26812686
double [] offsetX = new double[1];
26822687
double [] offsetY = new double [1];
26832688
double [] startX = new double[1];
26842689
double [] startY = new double [1];
2685-
if (GTK.gtk_gesture_drag_get_start_point(dragGesture, startX, startY)) {
2686-
GTK.gtk_gesture_drag_get_offset(dragGesture, offsetX, offsetY);
2687-
if (GTK3.gtk_drag_check_threshold(handle, (int)startX[0], (int) startY[0], (int) startX[0]
2688-
+ (int) offsetX[0], (int) startY[0] + (int) offsetY[0])) {
2689-
dragging = true;
2690-
}
2691-
} else {
2690+
if (!GTK.gtk_gesture_drag_get_start_point(dragGesture, startX, startY)) {
26922691
return false;
26932692
}
2694-
// Block until mouse was released or drag was detected, see Bug 515396.
2695-
while (true) {
2696-
if (!mouseDown) {
2697-
return false;
2698-
}
2699-
if (dragBegun) {
2700-
return true;
2701-
}
2693+
2694+
GTK.gtk_gesture_drag_get_offset(dragGesture, offsetX, offsetY);
2695+
if (GTK3.gtk_drag_check_threshold(handle, (int)startX[0], (int) startY[0], (int) startX[0]
2696+
+ (int) offsetX[0], (int) startY[0] + (int) offsetY[0])) {
2697+
return true;
27022698
}
2699+
2700+
return false;
27032701
} else {
2702+
boolean dragging = false;
27042703
boolean quit = false;
27052704
//428852 DND workaround for GTK3.
27062705
//Gtk3 no longer sends motion events on the same control during thread sleep
@@ -2801,8 +2800,8 @@ boolean dragDetect (int x, int y, boolean filter, boolean dragOnTimeout, boolean
28012800
}
28022801
gdk_event_free (eventPtr);
28032802
}
2803+
return dragging;
28042804
}
2805-
return dragging;
28062805
}
28072806

28082807
boolean filterKey (long event) {
@@ -3429,7 +3428,6 @@ void gtk_style_context_get_border (long context, int state, GtkBorder padding) {
34293428
@Override
34303429
void gtk_gesture_press_event (long gesture, int n_press, double x, double y, long event) {
34313430
mouseDown = true;
3432-
dragBegun = false;
34333431

34343432
int eventButton = GDK.gdk_button_event_get_button(event);
34353433
int eventTime = GDK.gdk_event_get_time(event);
@@ -3473,7 +3471,6 @@ long gtk_button_press_event (long widget, long event) {
34733471

34743472
long gtk_button_press_event (long widget, long event, boolean sendMouseDown) {
34753473
mouseDown = true;
3476-
dragBegun = false;
34773474

34783475
double [] eventX = new double [1];
34793476
double [] eventY = new double [1];
@@ -4109,10 +4106,6 @@ long gtk_mnemonic_activate (long widget, long arg1) {
41094106

41104107
@Override
41114108
void gtk4_motion_event(long controller, double x, double y, long event) {
4112-
if (mouseDown) {
4113-
dragBegun = true;
4114-
}
4115-
41164109
if (this == display.currentControl && (hooks(SWT.MouseHover) || filters(SWT.MouseHover))) {
41174110
display.addMouseHoverTimeout(handle);
41184111
}
@@ -4143,9 +4136,6 @@ void gtk4_motion_event(long controller, double x, double y, long event) {
41434136
@Override
41444137
long gtk_motion_notify_event (long widget, long event) {
41454138
int result;
4146-
if (mouseDown) {
4147-
dragBegun = true;
4148-
}
41494139

41504140
double[] eventX = new double[1];
41514141
double[] eventY = new double[1];
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2022 Syntevo and others.
3+
*
4+
* This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License 2.0
6+
* which accompanies this distribution, and is available at
7+
* https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
*
11+
* Contributors:
12+
* Syntevo - initial API and implementation
13+
*******************************************************************************/
14+
15+
package org.eclipse.swt.tests.manual;
16+
17+
import org.eclipse.swt.SWT;
18+
import org.eclipse.swt.dnd.*;
19+
import org.eclipse.swt.layout.*;
20+
import org.eclipse.swt.widgets.*;
21+
22+
public class Issue0247_WaylandDragDropGetsStuck {
23+
public static void main(String[] args) {
24+
final Display display = new Display ();
25+
26+
final Shell shell = new Shell (display);
27+
shell.setLayout (new GridLayout (1, true));
28+
29+
Label hint = new Label (shell, 0);
30+
hint.setText (
31+
"1) Use Linux with Wayland\n" +
32+
"2) Drag TreeItem\n" +
33+
"3) Issue 0247: Dragging begins immediately when mouse is moved without any move threshold\n" +
34+
"4) Move mouse over TreeItem slowly and double-click once in a while\n" +
35+
"5) Issue 0247: Sometimes, counter will grow, indicating that there's an unfinished drag.\n" +
36+
" You will also see it by DRAGGING indicator being stuck.\n"
37+
);
38+
39+
Tree control = new Tree (shell, SWT.BORDER);
40+
for (int i = 0; i < 5; i++) {
41+
new TreeItem(control, 0).setText("TreeItem #" + i);
42+
}
43+
44+
Label labelIsDrag = new Label(shell, 0);
45+
labelIsDrag.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
46+
47+
final int[] numActiveDrags = new int[1];
48+
DragSource dragSource = new DragSource (control, DND.DROP_MOVE | DND.DROP_COPY);
49+
dragSource.setTransfer (TextTransfer.getInstance ());
50+
dragSource.addDragListener (new DragSourceListener() {
51+
@Override
52+
public void dragStart(DragSourceEvent event) {
53+
labelIsDrag.setText("DRAGGING");
54+
numActiveDrags[0]++;
55+
}
56+
57+
@Override
58+
public void dragSetData(DragSourceEvent event) {
59+
event.data = "Data";
60+
}
61+
62+
@Override
63+
public void dragFinished(DragSourceEvent event) {
64+
numActiveDrags[0]--;
65+
labelIsDrag.setText("" + numActiveDrags[0]);
66+
}
67+
});
68+
69+
shell.pack ();
70+
shell.open ();
71+
72+
while (!shell.isDisposed ()) {
73+
if (!display.readAndDispatch ()) {
74+
display.sleep ();
75+
}
76+
}
77+
78+
display.dispose ();
79+
}
80+
}

0 commit comments

Comments
 (0)