From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Tassilo Horn Newsgroups: gmane.emacs.devel Subject: On the pains of using child-frames / posframes Date: Sat, 17 Jul 2021 22:18:51 +0200 Message-ID: <871r7wskbl.fsf@gnu.org> References: <20210715105707.10057.54746@vcs0.savannah.gnu.org> <20210715105709.4F14820D13@vcs0.savannah.gnu.org> Mime-Version: 1.0 Content-Type: text/plain Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="15794"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: mu4e 1.5.13; emacs 28.0.50 Cc: Stefan Monnier , emacs-devel@gnu.org To: Daniel Mendler Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Sat Jul 17 23:27:46 2021 Return-path: Envelope-to: ged-emacs-devel@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1m4rqc-0003xK-JB for ged-emacs-devel@m.gmane-mx.org; Sat, 17 Jul 2021 23:27:46 +0200 Original-Received: from localhost ([::1]:35630 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1m4rqa-0004Px-I2 for ged-emacs-devel@m.gmane-mx.org; Sat, 17 Jul 2021 17:27:44 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:48904) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1m4rpu-0003jh-77 for emacs-devel@gnu.org; Sat, 17 Jul 2021 17:27:02 -0400 Original-Received: from fencepost.gnu.org ([2001:470:142:3::e]:57416) by eggs.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1m4rps-0000h1-Pj; Sat, 17 Jul 2021 17:27:00 -0400 Original-Received: from auth1-smtp.messagingengine.com ([66.111.4.227]:34297) by fencepost.gnu.org with esmtpsa (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1m4rps-00084T-N1; Sat, 17 Jul 2021 17:27:00 -0400 Original-Received: from compute4.internal (compute4.nyi.internal [10.202.2.44]) by mailauth.nyi.internal (Postfix) with ESMTP id DB66F27C0054; Sat, 17 Jul 2021 17:26:59 -0400 (EDT) Original-Received: from mailfrontend2 ([10.202.2.163]) by compute4.internal (MEProxy); Sat, 17 Jul 2021 17:26:59 -0400 X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvtddrvdehgdekudcutefuodetggdotefrodftvf curfhrohhfihhlvgemucfhrghsthforghilhdpqfgfvfdpuffrtefokffrpgfnqfghnecu uegrihhlohhuthemuceftddtnecusecvtfgvtghiphhivghnthhsucdlqddutddtmdenuc fjughrpehffgfhvffuffgjkfggtgesthdtredttdertdenucfhrhhomhepvfgrshhsihhl ohcujfhorhhnuceothhsughhsehgnhhurdhorhhgqeenucggtffrrghtthgvrhhnpeffhf dtkeegheekfeejvefhleeljeekhfevheffkeefudegkefgleejkeejgfekteenucffohhm rghinhepghhithhhuhgsrdgtohhmpdhgnhhomhgvrdhorhhgpdhgnhhurdhorhhgnecuve hluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomhepthhhohhrnhdo mhgvshhmthhprghuthhhphgvrhhsohhnrghlihhthidqkeeijeefkeejkeegqdeifeehvd elkedqthhsughhpeepghhnuhdrohhrghesfhgrshhtmhgrihhlrdhfmh X-ME-Proxy: Original-Received: by mail.messagingengine.com (Postfix) with ESMTPA; Sat, 17 Jul 2021 17:26:58 -0400 (EDT) In-reply-to: X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Original-Sender: "Emacs-devel" Xref: news.gmane.io gmane.emacs.devel:271344 Archived-At: Daniel Mendler writes: > Not yet, for now I am only collecting and marking hacks and > workarounds in the code. There are already many workarounds for bugs > in child frames. The same applies to the Posframe package. Indeed, posframe and corfu share a quite a bunch of workarounds for making child-frames working as intended. In corfu.el those workarounds are nicely annotated, so here is a summary: The `no-accept-focus' frame parameter doesn't work reliably, e.g., it doesn't work for me in Sway (a wayland window manager). I've reported that as a sway bug, and a sway dev told me that this is probably a bug in their Xwayland implementation. But he also said that the GTK function gtk_window_set_accept_focus used by emacs to indicate that a frame shouldn't receive focus in gtk builds is a no-op for wayland, and that wayland doesn't really have a concept of non-focusable windows. So no-accept-focus will never work when emacs is built as a native wayland application, i.e., with the pgtk branch. Corfu works around that by redirecting focus back to the parent frame (posframe does the same), and ignoring mouse-clicks in the buffer shown by the child-frame. --8<---------------cut here---------------start------------->8--- (defun corfu--popup-redirect-focus () "Redirect focus from popup." (redirect-frame-focus corfu--frame (frame-parent corfu--frame))) ;;; XXX HACK install redirect focus hook (add-hook 'pre-command-hook #'corfu--popup-redirect-focus nil 'local) (defvar corfu--mouse-ignore-map (let ((map (make-sparse-keymap))) (dolist (k '(mouse-1 down-mouse-1 drag-mouse-1 double-mouse-1 triple-mouse-1 mouse-2 down-mouse-2 drag-mouse-2 double-mouse-2 triple-mouse-2 mouse-3 down-mouse-3 drag-mouse-3 double-mouse-3 triple-mouse-3 mouse-4 down-mouse-4 drag-mouse-4 double-mouse-4 triple-mouse-4 mouse-5 down-mouse-5 drag-mouse-5 double-mouse-5 triple-mouse-5 mouse-6 down-mouse-6 drag-mouse-6 double-mouse-6 triple-mouse-6 mouse-7 down-mouse-7 drag-mouse-7 double-mouse-7 triple-mouse-7)) (define-key map (vector k) #'ignore)) map) "Ignore all mouse clicks.") ;;; XXX HACK install mouse ignore map (use-local-map corfu--mouse-ignore-map) --8<---------------cut here---------------end--------------->8--- With GTK builds and Gnome / Cinnamon, one needs the following hack to make the child-frame resize properly: --8<---------------cut here---------------start------------->8--- (let* (... (x-gtk-resize-child-frames (let ((case-fold-search t)) (and ;; XXX HACK to fix resizing on gtk3/gnome taken from posframe.el ;; More information: ;; * https://github.com/minad/corfu/issues/17 ;; * https://gitlab.gnome.org/GNOME/mutter/-/issues/840 ;; * https://lists.gnu.org/archive/html/emacs-devel/2020-02/msg00001.html (string-match-p "gtk3" system-configuration-features) (string-match-p "gnome\\|cinnamon" (or (getenv "XDG_CURRENT_DESKTOP") (getenv "DESKTOP_SESSION") "")) 'resize-mode))) --8<---------------cut here---------------end--------------->8--- Some workarounds against flicker, and a case in which the order of setting a face attribute and setting the frame parameters makes a difference for no obvious reason. --8<---------------cut here---------------start------------->8--- ;; XXX HACK Setting the same frame-parameter/face-background is not a nop (BUG!). ;; Check explicitly before applying the setting. ;; Without the check, the frame flickers on Mac. ;; XXX HACK We have to apply the face background before adjusting the frame parameter, ;; otherwise the border is not updated (BUG!). (let* ((face (if (facep 'child-frame-border) 'child-frame-border 'internal-border)) (new (face-attribute 'corfu-border :background))) (unless (equal (face-attribute face :background corfu--frame) new) (set-face-background face new corfu--frame))) (let ((new (face-attribute 'corfu-background :background))) (unless (equal (frame-parameter corfu--frame 'background-color) new) (set-frame-parameter corfu--frame 'background-color new))) --8<---------------cut here---------------end--------------->8--- More flicker avoidance. --8<---------------cut here---------------start------------->8--- ;; XXX HACK Make the frame invisible before moving the popup from above to below the line in ;; order to avoid flicker. (unless (eq (< (cdr (frame-position corfu--frame)) yb) (< y yb)) (make-frame-invisible corfu--frame)) (set-frame-size corfu--frame width height t) (set-frame-position corfu--frame x y) (make-frame-visible corfu--frame))) --8<---------------cut here---------------end--------------->8--- It seems like some overlays can make `posn-at-point' return wrong y-values. --8<---------------cut here---------------start------------->8--- ;;; XXX HACK On Emacs 28 y-coordinate position computation is wrong if ;;; there exists a flymake underline overlay at that point. Therefore ;;; compute the y-coordinate at the line beginning. (x (or (car (posn-x-y (posn-at-point pos))) 0)) (y (save-excursion (goto-char pos) (beginning-of-line) (or (cdr (posn-x-y (posn-at-point))) 0)))) --8<---------------cut here---------------end--------------->8--- Also, the number of frame parameters one needs to apply for a typical child-frame whose intent is to display completions, tooltips, or inline help is quite large and nothing which one can write from the top of one's head. --8<---------------cut here---------------start------------->8--- (defvar corfu--frame-parameters '((no-accept-focus . t) (no-focus-on-map . t) (min-width . t) (min-height . t) (width . 0) (height . 0) (border-width . 0) (child-frame-border-width . 1) (left-fringe . 0) (right-fringe . 0) (vertical-scroll-bars . nil) (horizontal-scroll-bars . nil) (menu-bar-lines . 0) (tool-bar-lines . 0) (tab-bar-lines . 0) (no-other-frame . t) (unsplittable . t) (undecorated . t) (cursor-type . nil) (minibuffer . nil) (visibility . nil) (no-special-glyphs . t) (desktop-dont-save . t)) "Default child frame parameters.") --8<---------------cut here---------------end--------------->8--- Ditto for the buffer one's going to display in the child-frame. --8<---------------cut here---------------start------------->8--- (defvar corfu--buffer-parameters '((mode-line-format . nil) (header-line-format . nil) (tab-line-format . nil) (frame-title-format . "") (truncate-lines . t) (cursor-in-non-selected-windows . nil) (cursor-type . nil) (show-trailing-whitespace . nil) (display-line-numbers . nil) (left-fringe-width . nil) (right-fringe-width . nil) (left-margin-width . 0) (right-margin-width . 0) (fringes-outside-margins . 0) (buffer-read-only . t)) "Default child frame buffer parameters.") --8<---------------cut here---------------end--------------->8--- This all looks to me as if child-frames should probably have some dedicated API in vanilla emacs so that using them becomes a bit easier and less verbose. Also, that packages like corfu and posframe have to duplicate each other's workarounds for several issues like the unreliability of `no-accept-focus' doesn't seem right. If there are issues on certain supported platforms, those are better addressed in emacs itself. Bye, Tassilo