diff options
author | Alexander Gramiak <agrambot@gmail.com> | 2017-06-10 12:28:03 +0300 |
---|---|---|
committer | Eli Zaretskii <eliz@gnu.org> | 2017-06-10 12:28:03 +0300 |
commit | bdf41152af3434307218ac2863b737c4486f740e (patch) | |
tree | d8c2275a5814aa0c3be2761c1282b30007551bc1 | |
parent | 187a71df596a331a23bf86ee314c12035f42aff2 (diff) | |
download | emacs-bdf41152af3434307218ac2863b737c4486f740e.tar.gz |
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)
-rw-r--r-- | lisp/frame.el | 69 | ||||
-rw-r--r-- | src/xmenu.c | 46 |
2 files changed, 108 insertions, 7 deletions
diff --git a/lisp/frame.el b/lisp/frame.el index 02871e0551d..dc7bb24bb3a 100644 --- a/lisp/frame.el +++ b/lisp/frame.el @@ -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)) diff --git a/src/xmenu.c b/src/xmenu.c index 28052491646..6c8a0c506cc 100644 --- a/src/xmenu.c +++ b/src/xmenu.c @@ -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"), |