The rabbit hole that is HiDPI... (empty menus / bug#31223 et al)

classic Classic list List threaded Threaded
10 messages Options
Reply | Threaded
Open this post in threaded view
|

The rabbit hole that is HiDPI... (empty menus / bug#31223 et al)

Tobias Bading-2
Hi-ho.

A few days ago I built Emacs on a new GNU/Linux box at home, based on
the emacs-26 branch that works fine at the office. Unfortunately, the
new Emacs at home shows a bug I haven't seen before, but others have by
the looks of it: there's bug#31223 (merged with bug#23672 and bug#28106)
as well as Ubuntu bug
https://bugs.launchpad.net/ubuntu/+source/emacs25/+bug/1695228.

Short summary of the bug:
Sometimes menus of the menu bar are completely empty if a menu is opened
by a mouse click. The 'Buffers' menu may be incorrect as well.
(When the menu bar is opened by pressing the F10 key, all menus are fine
and stay that way for a while.)

For me an easy recipe to reproduce this is (with GTK3 on a 4K monitor):
- emacs -Q
- C-x C-f RET
- Now you're in dired-mode. The menus 'Mark', 'Regexp', 'Immediate' and
'Subdir' are empty when opened with the mouse. (OTOH, F10 opens the
first menu and all menus are fine afterwards.)

What I've figured out so far:

Switching to the dired buffer calls set_frame_menubar() in xmenu.c with
deep_p == false, which calls xg_modify_menubar_widgets(). Due to the
deep_p == false the latter doesn't (re)build the entire GTK menu tree,
just the top level items in the menu bar itself, with empty menus for
new items. When a menu bar item is then clicked, nothing fills the menus
prior to GTK popping them up, which of course explains the bug. If you
press F10 on the other hand, x-menu-bar-open-internal() calls
set_frame_menubar() with deep_p == true and all is good in the world. So
an easy fix is to ignore argument deep_p and always "go deep" in
set_frame_menubar(). That indeed seems to work fine, but doesn't answer
why the heck this bug never bit me at work, but some people out there
already got bitten, some even years ago it seems.

So I dug a little deeper...

When a menu item is clicked, handle_one_xevent() is trying to figure out
whether the ButtonPress was meant for the menu bar by calling
x_menubar_window_to_frame(dpyinfo, event), which calls
xg_event_is_for_menubar() for every frame, which performs its "magic"
trying to determine whether the event's mouse coordinates are within the
bounding rectangle of a child of the GTK menu bar widget.

If everything goes according to plan (i.e. xg_event_is_for_menubar()
returns true and x_menubar_window_to_frame() in turn the frame
containing the menu bar) handle_one_xevent() saves the ButtonPress X11
event in the frame's output_data.x->saved_menu_event, relays a
MENU_BAR_ACTIVATE_EVENT to kbd_buffer_store_buffered_event() and lastly
drops the ButtonPress event for the time being. A bit later
kbd_buffer_get_event() picks up the MENU_BAR_ACTIVATE_EVENT and calls
x_activate_menubar(), which calls set_frame_menubar() with deep_p ==
true and thus rebuilds the entire GTK menu bar. The final step is a
XPutBackEvent() for the saved_menu_event so that the (hopefully correct)
menu may pop up asap.

This entire plan hinges on the "magic" in xg_event_is_for_menubar(), and
that's where the shit starts to hit the fan when HiDPI comes into play.
On my GNU/Linux system with a MATE desktop, a 4K monitor and the 'window
scaling factor' set to 'HiDPI' (2x) in the 'MATE Tweak' tool,
FRAME_MENUBAR_HEIGHT(f) seems to return only half of the actual menu bar
height in pixel. So the first way to lose your magic is to click into
the lower half of a menu item. If you click into the upper half, no luck
either, because now gtk_widget_intersect() fails due to bounding
rectangles (GtkAllocation) which seem to have halved sizes as well. And
then there's the next "event->xbutton.y < FRAME_MENUBAR_HEIGHT (f)" in
handle_one_xevent() just around the corner...

After all that digging, the initial easy "always go deep" fix in
set_frame_menubar() doesn't look so bad anymore... ;-)

But seriously... what would be the proper way to deal with HiDPI in
Emacs? Has anyone done any work on this?

I don't have much experience with GTK or HiDPI solutions on GNU/Linux in
general. From what I've debugged so far it seems that GTK works similar
to CSS, with some form of abstract pixels, which are mapped to 2x2
device pixels in my case. Since the low-level X11 APIs and events still
seem to use actual device pixels, would it make sense to look for the
source of the halved values returned by FRAME_MENUBAR_HEIGHT(f) et al
and multiply these by two in my case?

I'll keep digging in the Emacs sources and the GTK documentation (or
sources) to learn a bit more...

See you around,
Tobias


Reply | Threaded
Open this post in threaded view
|

Re: The rabbit hole that is HiDPI... (empty menus / bug#31223 et al)

Tobias Bading-2
...and of course I forgot the little anecdote:

xg_event_is_for_menubar() calls gtk_widget_intersect(), which has
already been removed in GTK's Git master a year ago:
https://gitlab.gnome.org/GNOME/gtk/commit/bfa67371ecbcff144819618004b9f914a1431d42

Reasoning of the author:
It's unused inside gtk and as far as I can see also outside of it.
And it's been broken for a while now and nobody noticed.

:-D

PS: It would have been nice if the author had mentioned in what way it
was broken...


Reply | Threaded
Open this post in threaded view
|

Re: The rabbit hole that is HiDPI... (empty menus / bug#31223 et al)

Tobias Bading-2
In reply to this post by Tobias Bading-2
 > But seriously... what would be the proper way to deal with HiDPI in
 > Emacs? Has anyone done any work on this?
 > [...] would it make sense to look for the source of the halved values
 > returned by FRAME_MENUBAR_HEIGHT(f) et al and multiply these by two in
 > my case?

"Use the source, Luke!"
Please ignore my questions.
Found xg_get_scale() et al...
Working on a proper patch...


Reply | Threaded
Open this post in threaded view
|

Re: The rabbit hole that is HiDPI... (empty menus / bug#31223 et al)

Robert Pluim
In reply to this post by Tobias Bading-2
>>>>> On Tue, 26 Nov 2019 20:13:21 +0100, Tobias Bading <[hidden email]> said:

    Tobias> But seriously... what would be the proper way to deal with HiDPI in
    Tobias> Emacs? Has anyone done any work on this?

xg_get_scale(). And yes, which is why my hair is turning grey.

    Tobias> I don't have much experience with GTK or HiDPI solutions on GNU/Linux in
    Tobias> general. From what I've debugged so far it seems that GTK works similar
    Tobias> to CSS, with some form of abstract pixels, which are mapped to 2x2
    Tobias> device pixels in my case. Since the low-level X11 APIs and events still
    Tobias> seem to use actual device pixels, would it make sense to look for the
    Tobias> source of the halved values returned by FRAME_MENUBAR_HEIGHT(f) et al
    Tobias> and multiply these by two in my case?

Yes, if you have scaling in GTK the number of pixels as seen by GTK is
scaled up or down. Unfortunately too much code in Emacs still works in
device pixels. Who will rid us of this turbulent mix of X and GTK?

BTW, which distribution and desktop environment is this? Ubuntu xx
with Gnome?

Robert

PS. You might have more luck with emacs-27 and 'configure
--with-cairo'


Reply | Threaded
Open this post in threaded view
|

Re: The rabbit hole that is HiDPI... (empty menus / bug#31223 et al)

Tobias Bading-2
On 27.11.19 12:30, Robert Pluim wrote:
 >     Tobias> But seriously... what would be the proper way to deal
with HiDPI in
 >     Tobias> Emacs? Has anyone done any work on this?
 >
 > xg_get_scale(). And yes, which is why my hair is turning grey.

Thanks, already found it. No comments on the color of my hair... ;-)

 > Yes, if you have scaling in GTK the number of pixels as seen by GTK is
 > scaled up or down. Unfortunately too much code in Emacs still works in
 > device pixels. Who will rid us of this turbulent mix of X and GTK?

I'm trying to fix the mix.;-)
About to test whether two "req.height *= xg_get_scale (f);" in gtkutil.c
improve things.
(frame-monitor-workarea) is incorrect on my machine, too. Top is half of
what it should be.

 > BTW, which distribution and desktop environment is this? Ubuntu xx
 > with Gnome?

Ubuntu MATE Eoan.

 > PS. You might have more luck with emacs-27 and 'configure
 > --with-cairo'

Thanks for the hint. I'll give it a try when I have a patch that fixes
my current problems in 26.3.

Tobias


Reply | Threaded
Open this post in threaded view
|

Re: The rabbit hole that is HiDPI... (empty menus / bug#31223 et al)

Robert Pluim
>>>>> On Wed, 27 Nov 2019 13:29:32 +0100, Tobias Bading <[hidden email]> said:
    >> Yes, if you have scaling in GTK the number of pixels as seen by GTK is
    >> scaled up or down. Unfortunately too much code in Emacs still works in
    >> device pixels. Who will rid us of this turbulent mix of X and GTK?

    Tobias> I'm trying to fix the mix.;-)
    Tobias> About to test whether two "req.height *= xg_get_scale (f);" in gtkutil.c
    Tobias> improve things.
    Tobias> (frame-monitor-workarea) is incorrect on my machine, too. Top is half of
    Tobias> what it should be.

Hmm, thatʼs already a sign of things going
wrong. Fx_display_monitor_attributes_list does this:

          /* GTK returns scaled sizes for the workareas.  */
    #if GTK_CHECK_VERSION (3, 22, 0)
          scale = gdk_monitor_get_scale_factor (monitor);
    #elif defined HAVE_GTK3
          scale = gdk_screen_get_monitor_scale_factor (gscreen, i);
    #endif
          rec.width *= scale;
          rec.height *= scale;
          work.width *= scale;
          work.height *= scale;

Which should give you the right width and height, or the wrong width
and height, but not only one being wrong, unless gdk_monitor_get_workarea
is returning the wrong values.

    >> BTW, which distribution and desktop environment is this? Ubuntu xx
    >> with Gnome?

    Tobias> Ubuntu MATE Eoan.

Thatʼs pretty recent, so your gtk version is greater than 3.22, right?

Robert

Reply | Threaded
Open this post in threaded view
|

Re: The rabbit hole that is HiDPI... (empty menus / bug#31223 et al)

Tobias Bading-2
The menu bar problems are looking good so far.
A "req.height *= xg_get_scale (f)" was missing in menubar_map_cb() and
xg_update_frame_menubar(), right after calling
gtk_widget_get_preferred_size() for the menu bar widget. Plus scale
handling in xg_event_is_for_menubar() et voilà.

I have a look at the incorrect workarea top next. Thanks for you hint.

 > Thatʼs pretty recent, so your gtk version is greater than 3.22, right?

Yes, 3.24.12.

Tobias


Reply | Threaded
Open this post in threaded view
|

Re: The rabbit hole that is HiDPI... (empty menus / bug#31223 et al)

Robert Pluim
>>>>> On Wed, 27 Nov 2019 14:41:35 +0100, Tobias Bading <[hidden email]> said:

    Tobias> The menu bar problems are looking good so far.
    Tobias> A "req.height *= xg_get_scale (f)" was missing in menubar_map_cb() and
    Tobias> xg_update_frame_menubar(), right after calling
    Tobias> gtk_widget_get_preferred_size() for the menu bar widget. Plus scale
    Tobias> handling in xg_event_is_for_menubar() et voilà.

Curious that this wasn't needed before. Has something changed in GTK?

    >> Thatʼs pretty recent, so your gtk version is greater than 3.22, right?

    Tobias> Yes, 3.24.12.

And I have the same version, but have no problems with menus. Even
curiouser.

Robert

Reply | Threaded
Open this post in threaded view
|

Re: The rabbit hole that is HiDPI... (empty menus / bug#31223 et al)

Tobias Bading-2
On 27.11.19 16:12, Robert Pluim wrote:
 >>>>>> On Wed, 27 Nov 2019 14:41:35 +0100, Tobias Bading
<[hidden email]> said:
 >
 >     Tobias> The menu bar problems are looking good so far.
 >     Tobias> A "req.height *= xg_get_scale (f)" was missing in
menubar_map_cb() and
 >     Tobias> xg_update_frame_menubar(), right after calling
 >     Tobias> gtk_widget_get_preferred_size() for the menu bar widget.
Plus scale
 >     Tobias> handling in xg_event_is_for_menubar() et voilà.
 >
 > Curious that this wasn't needed before. Has something changed in GTK?

Nope, looks like a rather old bug. But it only affects HiDPI displays with
window scaling active (e.g. set in 'MATE Tweak' or similar in GNOME 2/3).
People using Xrandr to scale the entire display are probably not affected
either.

I just sent a patch to https://debbugs.gnu.org/cgi/bugreport.cgi?bug=31223

 >     >> Thatʼs pretty recent, so your gtk version is greater than
3.22, right?
 >
 >     Tobias> Yes, 3.24.12.
 >
 > And I have the same version, but have no problems with menus. Even
 > curiouser.

Do you use a HiDPI monitor with a window scaling factor of 2?

Tobias


Reply | Threaded
Open this post in threaded view
|

Re: The rabbit hole that is HiDPI... (empty menus / bug#31223 et al)

Robert Pluim
>>>>> On Wed, 27 Nov 2019 17:41:37 +0100, Tobias Bading <[hidden email]> said:

    Tobias> On 27.11.19 16:12, Robert Pluim wrote:
    >>>>>>> On Wed, 27 Nov 2019 14:41:35 +0100, Tobias Bading
    Tobias> <[hidden email]> said:
    >>
    >>      Tobias> The menu bar problems are looking good so far.
    >>      Tobias> A "req.height *= xg_get_scale (f)" was missing in
    Tobias> menubar_map_cb() and
    >>      Tobias> xg_update_frame_menubar(), right after calling
    >>      Tobias> gtk_widget_get_preferred_size() for the menu bar widget.
    Tobias> Plus scale
    >>      Tobias> handling in xg_event_is_for_menubar() et voilà.
    >>
    >> Curious that this wasn't needed before. Has something changed in GTK?

    Tobias> Nope, looks like a rather old bug. But it only affects HiDPI displays with
    Tobias> window scaling active (e.g. set in 'MATE Tweak' or similar in GNOME 2/3).
    Tobias> People using Xrandr to scale the entire display are probably not affected
    Tobias> either.

That describes my setup, although itʼs Fedora running Gnome, not
MATE. Perhaps I should boot into Ubuntu to compare.

    Tobias> I just sent a patch to https://debbugs.gnu.org/cgi/bugreport.cgi?bug=31223

Iʼll take a look.

    >>      >> Thatʼs pretty recent, so your gtk version is greater than
    Tobias> 3.22, right?
    >>
    >>      Tobias> Yes, 3.24.12.
    >>
    >> And I have the same version, but have no problems with menus. Even
    >> curiouser.

    Tobias> Do you use a HiDPI monitor with a window scaling factor of 2?

Yes.