1
0
mirror of https://git.savannah.gnu.org/git/emacs.git synced 2025-01-19 18:13:55 +00:00

Fix menu placement on multiple-display setups when using lwlib

* lwlib/xlwmenu.c (fit_to_screen):
(pop_up_menu): Adjust menu position based on dimensions of the
current monitor's workarea.  (bug#52809)

* src/xfns.c (x_get_monitor_attributes): Stop testing for the
RandR extension here.
(xlw_monitor_dimensions_at_pos_1):
(xlw_monitor_dimensions_at_pos): New functions.

* src/xterm.c (x_term_init): Query for the RandR extension when
connecting to a display.
* src/xterm.h (xlw_monitor_dimensions_at_pos): New prototype.
This commit is contained in:
Po Lu 2021-12-28 10:46:58 +08:00
parent 1a724cc2d2
commit b6b2f797d9
4 changed files with 195 additions and 34 deletions

View File

@ -1390,27 +1390,40 @@ fit_to_screen (XlwMenuWidget mw,
window_state *previous_ws,
Boolean horizontal_p)
{
unsigned int screen_width = WidthOfScreen (XtScreen (mw));
unsigned int screen_height = HeightOfScreen (XtScreen (mw));
int screen_width, screen_height;
int screen_x, screen_y;
#ifdef emacs
xlw_monitor_dimensions_at_pos (XtDisplay (mw), XtScreen (mw),
ws->x, ws->y, &screen_x, &screen_y,
&screen_width, &screen_height);
#else
screen_width = WidthOfScreen (XtScreen (mw));
screen_height = HeightOfScreen (XtScreen (mw));
screen_x = 0;
screen_y = 0;
#endif
/* 1 if we are unable to avoid an overlap between
this menu and the parent menu in the X dimension. */
int horizontal_overlap = 0;
if (ws->x < 0)
if (ws->x < screen_x)
ws->x = 0;
else if (ws->x + ws->width > screen_width)
else if (ws->x + ws->width > screen_x + screen_width)
{
if (!horizontal_p)
/* The addition of shadow-thickness for a sub-menu's position is
to reflect a similar adjustment when the menu is displayed to
the right of the invoking menu-item; it makes the sub-menu
look more `attached' to the menu-item. */
ws->x = previous_ws->x - ws->width + mw->menu.shadow_thickness;
ws->x = screen_x + (previous_ws->x
- ws->width
+ mw->menu.shadow_thickness);
else
ws->x = screen_width - ws->width;
if (ws->x < 0)
ws->x = screen_x + (screen_width - ws->width);
if (ws->x < screen_x)
{
ws->x = 0;
ws->x = screen_x;
horizontal_overlap = 1;
}
}
@ -1427,16 +1440,16 @@ fit_to_screen (XlwMenuWidget mw,
ws->y = previous_ws->y - ws->height;
}
if (ws->y < 0)
ws->y = 0;
else if (ws->y + ws->height > screen_height)
if (ws->y < screen_y)
ws->y = screen_y;
else if (ws->y + ws->height > screen_y + screen_height)
{
if (horizontal_p)
ws->y = previous_ws->y - ws->height;
ws->y = screen_y + (previous_ws->y - ws->height);
else
ws->y = screen_height - ws->height;
if (ws->y < 0)
ws->y = 0;
ws->y = screen_y + (screen_height - ws->height);
if (ws->y < screen_y)
ws->y = screen_y;
}
}
@ -2626,7 +2639,21 @@ pop_up_menu (XlwMenuWidget mw, XButtonPressedEvent *event)
int borderwidth = mw->menu.shadow_thickness;
Screen* screen = XtScreen (mw);
Display *display = XtDisplay (mw);
int screen_x;
int screen_y;
int screen_w;
int screen_h;
#ifdef emacs
xlw_monitor_dimensions_at_pos (display, screen, x, y,
&screen_x, &screen_y,
&screen_w, &screen_h);
#else
screen_x = 0;
screen_y = 0;
screen_w = WidthOfScreen (screen);
screen_h = HeightOfScreen (screen);
#endif
next_release_must_exit = 0;
mw->menu.inside_entry = NULL;
@ -2640,14 +2667,14 @@ pop_up_menu (XlwMenuWidget mw, XButtonPressedEvent *event)
x -= borderwidth;
y -= borderwidth;
if (x < borderwidth)
x = borderwidth;
if (x + w + 2 * borderwidth > WidthOfScreen (screen))
x = WidthOfScreen (screen) - w - 2 * borderwidth;
if (y < borderwidth)
y = borderwidth;
if (y + h + 2 * borderwidth> HeightOfScreen (screen))
y = HeightOfScreen (screen) - h - 2 * borderwidth;
if (x < screen_x + borderwidth)
x = screen_x + borderwidth;
if (x + w + 2 * borderwidth > screen_x + screen_w)
x = (screen_x + screen_w) - w - 2 * borderwidth;
if (y < screen_y + borderwidth)
y = screen_y + borderwidth;
if (y + h + 2 * borderwidth > screen_y + screen_h)
y = (screen_y + screen_h) - h - 2 * borderwidth;
mw->menu.popped_up = True;
if (XtIsShell (XtParent ((Widget)mw)))

View File

@ -5041,17 +5041,9 @@ x_get_monitor_attributes (struct x_display_info *dpyinfo)
(void) dpy; /* Suppress unused variable warning. */
#ifdef HAVE_XRANDR
int xrr_event_base, xrr_error_base;
bool xrr_ok = false;
xrr_ok = XRRQueryExtension (dpy, &xrr_event_base, &xrr_error_base);
if (xrr_ok)
{
XRRQueryVersion (dpy, &dpyinfo->xrandr_major_version,
&dpyinfo->xrandr_minor_version);
xrr_ok = ((dpyinfo->xrandr_major_version == 1
&& dpyinfo->xrandr_minor_version >= 2)
|| dpyinfo->xrandr_major_version > 1);
}
bool xrr_ok = ((dpyinfo->xrandr_major_version == 1
&& dpyinfo->xrandr_minor_version >= 2)
|| dpyinfo->xrandr_major_version > 1);
if (xrr_ok)
attributes_list = x_get_monitor_attributes_xrandr (dpyinfo);
@ -5076,6 +5068,129 @@ x_get_monitor_attributes (struct x_display_info *dpyinfo)
#endif /* !USE_GTK */
#ifdef USE_LUCID
/* This is used by the Lucid menu widget, but it's defined here so we
can make use of a great deal of existing code. */
static void
xlw_monitor_dimensions_at_pos_1 (struct x_display_info *dpyinfo,
Screen *screen, int src_x, int src_y,
int *x, int *y, int *width, int *height)
{
Lisp_Object attrs, tem, val;
#ifdef HAVE_XRANDR
#if RANDR_MAJOR > 1 || (RANDR_MAJOR == 1 && RANDR_MINOR >= 5)
int num_rr_monitors;
XRRMonitorInfo *rr_monitors;
/* If RandR 1.5 or later is available, use that instead, as some
video drivers don't report correct dimensions via other versions
of RandR. */
if (dpyinfo->xrandr_major_version > 1
|| (dpyinfo->xrandr_major_version == 1
&& dpyinfo->xrandr_minor_version >= 5))
{
rr_monitors = XRRGetMonitors (dpyinfo->display,
RootWindowOfScreen (screen),
True, &num_rr_monitors);
if (!rr_monitors)
goto fallback;
for (int i = 0; i < num_rr_monitors; ++i)
{
if (rr_monitors[i].x <= src_x
&& src_x < (rr_monitors[i].x
+ rr_monitors[i].width)
&& rr_monitors[i].y <= src_y
&& src_y < (rr_monitors[i].y
+ rr_monitors[i].height))
{
*x = rr_monitors[i].x;
*y = rr_monitors[i].y;
*width = rr_monitors[i].width;
*height = rr_monitors[i].height;
XRRFreeMonitors (rr_monitors);
return;
}
}
XRRFreeMonitors (rr_monitors);
}
fallback:
#endif
#endif
attrs = x_get_monitor_attributes (dpyinfo);
for (tem = attrs; CONSP (tem); tem = XCDR (tem))
{
int sx, sy, swidth, sheight;
val = assq_no_quit (Qgeometry, XCAR (tem));
if (!NILP (val))
{
sx = XFIXNUM (XCAR (XCDR (val)));
sy = XFIXNUM (XCAR (XCDR (XCDR (val))));
swidth = XFIXNUM (XCAR (XCDR (XCDR (XCDR (val)))));
sheight = XFIXNUM (XCAR (XCDR (XCDR (XCDR (XCDR (val))))));
if (sx <= src_x && src_x < (sx + swidth)
&& sy <= src_y && src_y < (sy + swidth))
{
*x = sx;
*y = sy;
*width = swidth;
*height = sheight;
return;
}
}
}
*x = 0;
*y = 0;
*width = WidthOfScreen (screen);
*height = HeightOfScreen (screen);
}
void
xlw_monitor_dimensions_at_pos (Display *dpy, Screen *screen, int src_x,
int src_y, int *x, int *y, int *width, int *height)
{
struct x_display_info *dpyinfo = x_display_info_for_display (dpy);
XRectangle rect, workarea, intersection;
int dim_x, dim_y, dim_w, dim_h;
if (!dpyinfo)
emacs_abort ();
block_input ();
xlw_monitor_dimensions_at_pos_1 (dpyinfo, screen, src_x, src_y,
&dim_x, &dim_y, &dim_w, &dim_h);
rect.x = dim_x;
rect.y = dim_y;
rect.width = dim_w;
rect.height = dim_h;
if (!x_get_net_workarea (dpyinfo, &workarea))
memset (&workarea, 0, sizeof workarea);
unblock_input ();
if (!gui_intersect_rectangles (&rect, &workarea, &intersection))
{
*x = 0;
*y = 0;
*width = 0;
*height = 0;
return;
}
*x = intersection.x;
*y = intersection.y;
*width = intersection.width;
*height = intersection.height;
}
#endif
DEFUN ("x-display-monitor-attributes-list", Fx_display_monitor_attributes_list,
Sx_display_monitor_attributes_list,
0, 1, 0,

View File

@ -46,6 +46,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <X11/extensions/XInput2.h>
#endif
#ifdef HAVE_XRANDR
#include <X11/extensions/Xrandr.h>
#endif
/* Load sys/types.h if not already loaded.
In some systems loading it twice is suicidal. */
#ifndef makedev
@ -14803,6 +14807,17 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name)
dpyinfo->xi2_version = minor;
#endif
#ifdef HAVE_XRANDR
int xrr_event_base, xrr_error_base;
bool xrr_ok = false;
xrr_ok = XRRQueryExtension (dpy, &xrr_event_base, &xrr_error_base);
if (xrr_ok)
{
XRRQueryVersion (dpy, &dpyinfo->xrandr_major_version,
&dpyinfo->xrandr_minor_version);
}
#endif
#ifdef HAVE_XKB
dpyinfo->xkb_desc = XkbGetMap (dpyinfo->display,
XkbAllComponentsMask,

View File

@ -1240,6 +1240,10 @@ extern void x_change_tool_bar_height (struct frame *, int);
extern void x_implicitly_set_name (struct frame *, Lisp_Object, Lisp_Object);
extern void x_set_scroll_bar_default_width (struct frame *);
extern void x_set_scroll_bar_default_height (struct frame *);
#ifdef USE_LUCID
extern void xlw_monitor_dimensions_at_pos (Display *, Screen *, int, int,
int *, int *, int *, int *);
#endif
/* Defined in xselect.c. */