Fix the placement of GTK menus on multi-monitor systems

menu_position_func did not properly use the current monitor's
resolution.  Also see commit '2016-02-06 22:12:53 +0100'.

* lisp/frame.el (frame-monitor-attribute, frame-monitor-geometry)
(frame-monitor-workarea): New functions.

* src/xmenu.c (menu_position_func): Take into account the workarea of
the monitor that contains the mouse.  (Bug#23568)
This commit is contained in:
Alexander Gramiak 2017-06-10 12:28:03 +03:00 committed by Eli Zaretskii
parent 187a71df59
commit bdf41152af
2 changed files with 108 additions and 7 deletions

View file

@ -1498,6 +1498,75 @@ keys and their meanings."
for frames = (cdr (assq 'frames attributes))
if (memq frame frames) return attributes))
(defun frame-monitor-attribute (attribute &optional frame x y)
"Return the value of ATTRIBUTE on FRAME's monitor.
If FRAME is omitted or nil, use currently selected frame.
By default, the current monitor is the physical monitor
dominating the selected frame. A frame is dominated by a
physical monitor when either the largest area of the frame
resides in the monitor, or the monitor is the closest to the
frame if the frame does not intersect any physical monitors.
If X and Y are both numbers, then ignore the value of FRAME; the
monitor is determined to be the physical monitor that contains
the pixel coordinate (X, Y).
See `display-monitor-attributes-list' for the list of attribute
keys and their meanings."
(if (and (numberp x)
(numberp y))
(cl-loop for monitor in (display-monitor-attributes-list)
for geometry = (alist-get 'geometry monitor)
for min-x = (pop geometry)
for min-y = (pop geometry)
for max-x = (+ min-x (pop geometry))
for max-y = (+ min-y (car geometry))
when (and (<= min-x x)
(< x max-x)
(<= min-y y)
(< y max-y))
return (alist-get attribute monitor))
(alist-get attribute (frame-monitor-attributes frame))))
(defun frame-monitor-geometry (&optional frame x y)
"Return the geometry of FRAME's monitor.
FRAME can be a frame name, a terminal name, or a frame.
If FRAME is omitted or nil, use the currently selected frame.
By default, the current monitor is said to be the physical
monitor dominating teh selected frame. A frame is dominated by
a physical monitor when either the largest area of the frame resides
in the monitor, or the monitor is the closest to the frame if the
frame does not intersect any physical monitors.
If X and Y are both numbers, then ignore the value of FRAME; the
monitor is determined to be the physical monitor that contains
the pixel coordinate (X, Y).
See `display-monitor-attributes-list' for information on the
geometry attribute."
(frame-monitor-attribute 'geometry frame x y))
(defun frame-monitor-workarea (&optional frame x y)
"Return the workarea of FRAME's monitor.
FRAME can be a frame name, a terminal name, or a frame.
If FRAME is omitted or nil, use currently selected frame.
By default, the current monitor is said to be the physical
monitor dominating the selected frame. A frame is dominated by
a physical monitor when either the largest area of the frame resides
in the monitor, or the monitor is the closest to the frame if the
frame does not intersect any physical monitors.
If X and Y are both numbers, then ignore the value of FRAME; the
monitor is determined to be the physical monitor that contains
the pixel coordinate (X, Y).
See `display-monitor-attributes-list' for information on the
workarea attribute."
(frame-monitor-attribute 'workarea frame x y))
(declare-function x-frame-list-z-order "xfns.c" (&optional display))
(declare-function w32-frame-list-z-order "w32fns.c" (&optional display))
(declare-function ns-frame-list-z-order "nsfns.m" (&optional display))

View file

@ -1160,9 +1160,37 @@ menu_position_func (GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer
{
struct next_popup_x_y *data = user_data;
GtkRequisition req;
struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (data->f);
int disp_width = x_display_pixel_width (dpyinfo);
int disp_height = x_display_pixel_height (dpyinfo);
int max_x = -1;
int max_y = -1;
Lisp_Object frame, workarea;
XSETFRAME (frame, data->f);
/* TODO: Get the monitor workarea directly without calculating other
items in x-display-monitor-attributes-list. */
workarea = call3 (Qframe_monitor_workarea,
Qnil,
make_number (data->x),
make_number (data->y));
if (CONSP (workarea))
{
int min_x, min_y;
min_x = XINT (XCAR (workarea));
min_y = XINT (Fnth (make_number (1), workarea));
max_x = min_x + XINT (Fnth (make_number (2), workarea));
max_y = min_y + XINT (Fnth (make_number (3), workarea));
}
if (max_x < 0 || max_y < 0)
{
struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (data->f);
max_x = x_display_pixel_width (dpyinfo);
max_y = x_display_pixel_height (dpyinfo);
}
*x = data->x;
*y = data->y;
@ -1170,10 +1198,10 @@ menu_position_func (GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer
/* Check if there is room for the menu. If not, adjust x/y so that
the menu is fully visible. */
gtk_widget_get_preferred_size (GTK_WIDGET (menu), NULL, &req);
if (data->x + req.width > disp_width)
*x -= data->x + req.width - disp_width;
if (data->y + req.height > disp_height)
*y -= data->y + req.height - disp_height;
if (data->x + req.width > max_x)
*x -= data->x + req.width - max_x;
if (data->y + req.height > max_y)
*y -= data->y + req.height - max_y;
}
static void
@ -2361,6 +2389,10 @@ syms_of_xmenu (void)
DEFSYM (Qdebug_on_next_call, "debug-on-next-call");
defsubr (&Smenu_or_popup_active_p);
#ifdef USE_GTK
DEFSYM (Qframe_monitor_workarea, "frame-monitor-workarea");
#endif
#if defined (USE_GTK) || defined (USE_X_TOOLKIT)
defsubr (&Sx_menu_bar_open_internal);
Ffset (intern_c_string ("accelerate-menu"),