Subject: bug#28620: Interact directly on Emacs bug#28620: mouse drag event records wrong release window

From: Robert Weiner <[email-masked]>
Date: Wed, 11 Oct 2017 21:35:13 -0400

I now have drags working across frames (after manipulating the drag button
release event data to include the proper window and coordinates, though
only in Lisp right now).  As shown in the last message, I also have a
function that finds the proper Emacs window given some display
coordinates.  The only remaining problem seems to be that none of this
accounts for external application windows that may be obscuring an Emacs
frame.

So if I have 2 frames, f1 and f2, and a Chrome web browser window that is
atop f2, then if I drag from f1 into Chrome above f2, my drag release code
reports that the release window is in f2 rather than nil, as it should be.
I am on macOS which uses click to focus, so Emacs still gets the release
event since Chrome has not been selected with a click.

Is there any way to deal with external window z-order layering such that
one can tell within Emacs whether the topmost OS-level window at an
absolute mouse position is an Emacs frame or not?

Thanks,

Bob

On Wed, Oct 11, 2017 at 2:49 PM, Robert Weiner <[email-masked]> wrote:

> On Wed, Oct 11, 2017 at 1:16 PM, Robert Weiner <[email-masked]> wrote:
>
>>
>> ​Martin wrote:​
>>
>>> Take the position of the event-end (if it's a frame) and translate it
>>> into absolute screen coordinates (the Elisp manual should give you
>>> enough clues to do that).  Or, try ‘mouse-absolute-pixel-position’ - it
>>> should give you the screen position of the mouse at that time so you can
>>> ignore the event completely.
>>>
>>> Then walk all your windows and compare that position with whatever
>>> ‘window-absolute-pixel-edges’ returns for that window.  If you have two
>>> or more positives, run ‘frame-list-z-order’ and compare the result
>>> against those windows' frames.  No hands, IMHO.
>>> ​​
>>>
>> ​​
>
> ​  Eli wrote:
>     ​​
> Why cannot you compute the frame at button release using the a
> ​​
> lgorithm
>          proposed by Martin, given the mouse position at button release?​
>
>
>>
>>> ​​
>>>
>> I w
>> ​​
>> rote:
>>   ​
>> ​​
>> frame-list-z-order is Emacs 26 only; I need something that works with
>> older versions.​
>> ​​
>> ​​
>> I'll see if I can make this work under Emacs 26 and then we can
>> contemplate a solution that would apply to earlier versions.
>> ​​
>> Thanks for the reminder.  It does still seem to me that there should be a
>> function that takes a mouse position and returns
>> ​​
>> the top-most Emacs window that the position is in or nil.  I'll work on
>> it.
>>
> ​​
> And now there is such a function.  It was easier than I expected thanks
> to Martin's pointers.  Now how can we make this work (replacing
> frame-list-z-order) for Emacs versions prior to 26?  -- Bob
>
> (defun window-at-absolute-pixel-position (&optional position)
>   "Return the top-most Emacs window at optional POSITION ((X . Y) in
> pixels) or mouse position.
> If POSITION is not in a window, return nil.  Considers all windows on the
> the same terminal
> display as the selected frame."
>   (interactive)
>   (setq position (or position (mouse-absolute-pixel-position)))
>   (let* ((top-to-bottom-frames (frame-list-z-order))
> (pos-x (car position))
> (pos-y (cdr position))
> edges left top right bottom
> frame
> in-frame
> window)
>     ;; First find top-most frame containing position.
>     (while (and (not in-frame) top-to-bottom-frames)
>       (setq frame (car top-to-bottom-frames)
>     top-to-bottom-frames (cdr top-to-bottom-frames))
>       ;; Check that in-frame is valid with frame-live-p since under macOS
>       ;; when position is outside a frame, in-frame could be invalid and
>       ;; frame-visible-p would trigger an error in that case.
>       (when (and (frame-live-p frame) (frame-visible-p frame))
> (setq edges (frame-edges frame)
>       left   (nth 0 edges)
>       top    (nth 1 edges)
>       right  (nth 2 edges)
>       bottom (nth 3 edges))
> (when (and (>= pos-x left) (<= pos-x right)
>    (>= pos-y top)  (<= pos-y bottom))
>   (setq in-frame frame))))
>     ;; If in-frame is found, find which of its windows contains
>     ;; position and return that.  The window-at call below requires
>     ;; character coordinates relative to in-frame, so compute them.
>     (setq pos-x (/ (- pos-x left) (frame-char-width in-frame))
>   pos-y (/ (- pos-y top) (frame-char-height in-frame))
>   window (window-at pos-x pos-y in-frame))
>     (if (called-interactively-p 'interactive)
>   (message "%s at absolute pixel position %s"
>    (or window "No Emacs window") position))
>     window))
>