Open WJCFerguson opened 5 years ago
The answer is probably no as it uses 'child frames'. From the Elisp reference:
Raising, lowering and restacking child frames (see Raising and Lowering) or changing the ‘z-group’ (see Position Parameters) of a child frame changes only the stacking order of child frames with the same parent.
There is a workaround mentioned on reddit:
(setq ivy-posframe-parameters '((parent-frame nil)))
Thanks @QiangF . I tried the solution and it seems working. @WJCFerguson could you verify that?
BTW, apart from this package you can already avoid resizing X windows on completion with (setq exwm-workspace-minibuffer-position 'bottom)
. But that makes a lot of other changes.
I can confirm that the parent-frame nil
fix does bring the posframe to above X windows, but for me with two monitors it results in the posframe being in badly positioned.
It appears that the frame is sent to an absolute coordinate using the desired x,y relative to the frame, so mine always ends up somewhere on my left monitor.
I assumed I might want to add the frame position to the posframe position, so I tried writing an advising function adding (frame-position (selected-frame))
to the result, but it turns out that call doesn't give me the frame position - perhaps that's an exwm bug?. Right now I'm getting (0 . 0)
for all frames, but in the past I've also had them give non-zero but incorrect positions (e.g. my right monitor frame thinks it's on the left and vice-versa). This may actually be the original cause of the posframe mis-positioning, or related to it.
(On hotplug exwm-randr-screen-change-hook
calls my function that sets my monitor layout with xrandr
using absolute positioning, and then sets exwm-randr-workspace-output-plist
to distribute frames across the monitors)
frame-position
returns the internal record of what Emacs think where it is. It may have something to do with EXWM as Emacs frames are not not managed here. Perhaps you can simply use (elt exwm-workspace--workareas exwm-workspace-current-index)
which returns [X Y WITH HEIGHT]
; all workspace frames are placed according to this after all.
@WJCFerguson , have you found a solution for multi-monitor setup? Seems that this snippet works fine for me:
(defun +ivy-posframe-display-exwm (str)
(ivy-posframe--display str
(lambda (info)
(let* ((workarea (elt exwm-workspace--workareas exwm-workspace-current-index))
(x (aref workarea 0))
(y (aref workarea 1))
(fw (aref workarea 2))
(fh (aref workarea 3))
(pw (plist-get info :posframe-width))
(ph (plist-get info :posframe-height)))
(cons (+ x (/ (- fw pw) 2)) (+ y (/ (- fh ph) 2)))))))
(setq ivy-posframe-display-functions-alist
'((t . +ivy-posframe-display-exwm))
ivy-posframe-parameters '((parent-frame nil)
(z-group . above)))
;; force set frame-position on every posframe display
(advice-add 'posframe--set-frame-position :before
(lambda (&rest args)
(setq-local posframe--last-posframe-pixel-position nil)))
Thank you @sarg, that does work. I didn't find time to get to a solution, so I'm grateful to get one from you.
I kind of wanted different positioning, so instead I did this to shift the standard function like so:
(defun jf/ivy-posframe-shift-display (return-value)
"Shift the RETURN-VALUE to account for the work area's position."
(let ((workarea (elt exwm-workspace--workareas exwm-workspace-current-index)))
(cons (+ (aref workarea 0) (car return-value))
(+ (aref workarea 1) (cdr return-value)))))
(advice-add 'posframe-poshandler-window-bottom-left-corner
:filter-return 'jf/ivy-posframe-shift-display)
It does require checking the right ivy-posframe-display-at-*
function in ivy-posframe
to find what posframe-poshandler-window-*
function to advise, based on what value you have for ivy-posframe-style
.
I've noticed minor bug: when I open posframe on secondary display then on every typed letter it'll jump for a moment to a primary display.
It happens because posframe-show
first does posframe--set-frame-size
and then posframe--set-position
(https://github.com/tumashu/posframe/blob/master/posframe.el#L533). The first is using fit-frame-to-buffer
which in fact can reposition the frame.
I'll try to pinpoint the reason why fit-frame-to-buffer
doesn't respect original frame position for multi-monitor setup.
I see that too.
I'm curious what makes posframe positioning work badly for exwm in the first place. Surely posframe
works fine for normal use of emacs in multiple windows (say, one instance with frames on two monitors)? So is there something about exwm that means the way posframe
normally does it fail - e.g. frame parameters being incorrect or something. I have to do paid-for work right now, but perhaps I can work this out at the weekend.
I've spent some time after paid-for work and seems that the bug lies in lisp/window.el
. Please see this patch: 0001-Fix-fit-frame-to-buffer-for-multi-monitor-setup.txt
With patched fit-frame-to-buffer
advice for posframe--set-frame-position
is not needed anymore.
The aforementioned patch has been merged into emacs-27: http://git.savannah.gnu.org/cgit/emacs.git/commit/?h=emacs-27&id=b42b894d1def7180ab715615116fe6af65b76bd8
@sarg, that's outstanding, thank you very much.
@sarg Thank you!
I see issues with ivy-posframe
when I set focus to follow the mouse:
(setq mouse-autoselect-window t
focus-follows-mouse 'autoraise)
It seems that #'show-posframe
always moves the mouse to the top left of the screen. This makes the childframe unresponsive if mouse-autoselect-window
is set and moving the mouse happens to activate another buffer.
I can somewhat mitigate this behaviour by modifying the call to posframe-show
in ivy-posframe--display
to add an :accept-focus t
argument. But as mentioned in the posframe-show
function docs, this gives some odd behaviour, notably the childframe becomes editable.
My current workaround is the following code which disables the mouse movement during the call to ivy-read and restores the mouse position afterwards (as a bonus). This seems to resolve the issues.
(defun advise-fn-suspend-follow-mouse (fn &rest args)
(let ((focus-follows-mouse nil)
(mouse-autoselect-window nil)
(pos (x-mouse-absolute-pixel-position)))
(unwind-protect
(apply fn args)
(x-set-mouse-absolute-pixel-position (car pos)
(cdr pos)))))
(advice-add #'ivy-posframe--read :around #'advise-fn-suspend-follow-mouse)
@sarg Thanks for the snippet you posted above; it works great! However, there is one issue:
When I trigger ivy-posframe from one of my secondary monitors after setting the frame font size below a certain threshold, the posframe appears on a different monitor (and off-center at that). It seems as if the center of the frame can't be calculated properly.
More details: I have a secondary monitor in portrait mode with a lower resolution than my main monitor. As such, I tend to set the frame font size on this frame to about half of what I use for my main monitor. The posframe is positioned correctly until I get below 147
, at which point it suddenly appears on a different monitor. Whether the posframe is completely within the bounds of this other frame or not seems to depend on the font size I use. The posframe font size is always the same and is the default font size.
Do you have any idea what might be happening and how I could fix this? Thanks for any input you may have!
For anyone reading this thread, I've found that setting the parent frame to nil after creating it (through advice) works better than setting it in the initial frame parameters:
(with-eval-after-load 'posframe
(define-advice posframe-show (:filter-return (frame) exwm-deparent)
(set-frame-parameter frame 'parent-frame nil)
frame))
It this is likely because posframe uses the parent frame when determining size/position.
I try to fix ivy-posframe in exwm problem, maybe useful:
I have update ivy-posframe, it should work well with exwm, at the moment, I use the below refposhandler,
it can improve future I think, PR is welcome :-)
(defun ivy-posframe-refposhandler-default (&optional frame)
"The default posframe refposhandler used by ivy-posframe."
(cond
;; EXWM environment
(exwm--connection
(or (ignore-errors
;; Need user install xwininfo.
(posframe-refposhandler-xwininfo frame))
;; FIXME: maybe exwm provide some function,
;; Which can get top-left of emacs.
(cons 0 0)))
(t nil)))
posframe need update too
ivy-posframe modifies
ivy
so that completions can be presented in a floating frame, rather than the minibuffer.This would be great with
exwm
, as it would allow completions that don't resize windows. However, onexwm-mode
windows the frame appears under the X window, i.e. invisible.I assume this is something about Z positioning, but I'm afraid I just don't know how to fix it(?). We have access to the frame parameters the
ivy-posframe
frame is created with (ivy-posframe-parameters
). I tried settingz-group
toabove
, but either I did it wrong, or it needs something else.Here's a config I tried:
Any ideas? Thanks.
Edit: Warning: you can't undo- not any more, just callivy-posframe
, so unfortunately it means restarting yourexwm
session to get rid of it.(ivy-posframe-mode nil)
.