emacs-diffs
[Top][All Lists]
Advanced

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

[Emacs-diffs] master bdf4115: Fix the placement of GTK menus on multi-mo


From: Eli Zaretskii
Subject: [Emacs-diffs] master bdf4115: Fix the placement of GTK menus on multi-monitor systems
Date: Sat, 10 Jun 2017 05:29:20 -0400 (EDT)

branch: master
commit bdf41152af3434307218ac2863b737c4486f740e
Author: Alexander Gramiak <address@hidden>
Commit: Eli Zaretskii <address@hidden>

    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)
---
 lisp/frame.el | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/xmenu.c   | 46 +++++++++++++++++++++++++++++++++------
 2 files changed, 108 insertions(+), 7 deletions(-)

diff --git a/lisp/frame.el b/lisp/frame.el
index 02871e0..dc7bb24 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 2805249..6c8a0c5 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"),



reply via email to

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