[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

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

From: Tobias Bading
Subject: The rabbit hole that is HiDPI... (empty menus / bug#31223 et al)
Date: Tue, 26 Nov 2019 20:13:21 +0100
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Thunderbird/68.1.2


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

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,

reply via email to

[Prev in Thread] Current Thread [Next in Thread]