dnd: keep the drop target visible when a drag starts from a window - #3852
dronov-dmitry wants to merge 1 commit into
Conversation
Clicking a Nemo window that does not have the input focus makes the window manager raise and focus it, so starting a drag from a window that sat behind another one - a terminal, say - covers the window the file is being dropped on for as long as the drag lasts. Undo that raise from the button press handler of the content views, i.e. in the same event dispatch the raise happened in, so it is never rendered, and only put the window back in front if the press turns out to be a plain click or a selection rectangle rather than a drag. The window is put back exactly where it was instead of at the bottom of the stack, by asking the window manager to restack it below the window that used to be above it (_NET_RESTACK_WINDOW); dropping onto one of our own folders therefore still works. When the drag ends over some other window, the input focus is handed to it, so afterwards this window behaves like any other background window and the next click on it raises it again. Whether a click would raise the window is sampled when the pointer enters it and when it loses focus. Crossing events delivered while a button is held are ignored: the raise caused by the click produces another EnterNotify, already with the focus, which would otherwise wipe out the answer just recorded and stop the window from stepping aside at all.
|
No thanks, just pin the window you want to keep on top temporarily. |
|
Understood, thanks for taking a look. For the record, in case it helps anyone landing here later: what the patch does is narrower than pinning. It only undoes the raise that the drag-initiating click itself made, and only when the window had been in front of the drop target before that click — so the stacking is left completely untouched in every other case. Pinning is a permanent stacking preference (the terminal then stays above Nemo even when that is not wanted), while this only concerns the few hundred milliseconds during which the raise hides the drop target. There is also a symptom pinning does not address: without the fix the covering window flickers on press (it goes up when the button goes down and back down when The branch stays at |
Summary
Starting a drag from a Nemo window that was sitting behind another window (a
terminal, typically) makes Nemo cover the window the file is being dragged to,
so the drop target disappears for as long as the drag lasts.
This undoes that raise for the duration of the drag, and hands the input focus
to the drop target once the drag has ended over some other window.
Root cause
Clicking a window that does not have the input focus is intercepted by the
window manager: Muffin raises and focuses it (its passive
XIGrabButtonon theframe,
meta_display_grab_focus_window_button()) and then replays the buttonpress to the client. So by the time Nemo sees the press, it has already been
raised above the drop target.
Nemo itself never raises its window — the only
gdk_window_focus()in a dragpath is the one in
start_stretching(). The raise comes entirely from thewindow manager.
Behaviour after the change
band_select_started, which fires in the same dispatch as the pressdisable-chrome)How it works
Whether the press raised the window is taken from the focus change it causes:
nemo_window_focus_in_event()notes focus arriving while a button is down,which can only mean the window manager raised and focused us because of that
click — and it is delivered before the button press itself, so
nemo_window_dnd_source_press()can rely on it. Focus taken any other way(window activation, a dialog) means the window is legitimately on top. The
flag is cleared on button release, on drag end, on focus loss and on a plain
pointer arrival, so it never survives into the next press.
The position to restore is remembered as the XID of the window stacked
directly above this one, recorded while nobody is pressing on us — the
EnterNotifythat follows the raise arrives once we are already on top, sorecording the window above us then would record "nothing". It is restored
with
_NET_RESTACK_WINDOWrather than with
gdk_window_lower(). Going to the bottom of the stack wouldput Nemo under windows that were below it before the click, and a drop onto
one of its own folders would then land on one of those windows instead.
The request is sent twice, with source indication 1 (application) and 2
(pager): Muffin ignores anything that is not attributed to a pager
(
handle_net_restack_window()), other window managers may do the opposite,and the request is idempotent. Sending it as a pager is not strictly what
EWMH intends an application to do — it is the only value Muffin accepts, and
there is no ICCCM equivalent, because
XConfigureWindow()withCWSiblingfails with
BadMatchfor a reparented client window.nemo_window_dnd_source_press()/nemo_window_dnd_source_release()arehooked to
button-press-event/button-release-eventof both content views;the icon view additionally hooks
band_select_started. The list view onlysteps aside when the press lands on a row (
gtk_tree_view_get_path_at_pos),so a selection rectangle there does not touch the window at all.
nemo_window_dnd_step_aside()(ondrag-begin) records that a drag isrunning, re-sends the restack and falls back to
gdk_window_lower()if thewindow manager does not support restacking against a sibling.
nemo_window_dnd_source_end()(ondrag-end) activates the window under thepointer through
_NET_ACTIVE_WINDOW, which is also what drops Nemo's focus.Everything except the two
gdk_window_*calls is behind#ifdef GDK_WINDOWING_X11; on Wayland the compositor owns stacking, so the feature is ano-op there.
Testing
Manual, on X11 with Muffin:
clicking. Drag it onto the terminal — Nemo must not cover the terminal at
any point, including the instant the button is pressed.
receives the input focus.
window stays in front.
drop lands on the folder, not on whatever window is behind Nemo.
must not move at all, and the window that was behind it must stay behind it
(an earlier revision of this branch had a regression here: a stale "this
press raised us" flag pushed Nemo back under a window it was no longer
under).
Build and test:
meson setup builddir ninja -C builddir meson test -C builddir ./builddir/src/nemo --no-desktopAffected files
src/nemo-window.cdnd_step_aside/dnd_source_press/dnd_source_release/dnd_source_endsrc/nemo-window.hsrc/nemo-window-private.hNemoWindowDetailssrc/nemo-icon-view.cdrag-begin,drag-end,button-press-event,button-release-event,band_select_startedhookssrc/nemo-list-view.csrc/meson.buildx11dependency forXQueryPointer()/XGetWindowProperty()