unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
From: Alex <agrambot@gmail.com>
To: Eli Zaretskii <eliz@gnu.org>
Cc: 23568@debbugs.gnu.org
Subject: bug#23568: 25.0.94; Mode line menus appear incorrectly in some monitor configurations
Date: Mon, 05 Jun 2017 02:03:45 -0600	[thread overview]
Message-ID: <87vaoauahq.fsf@gmail.com> (raw)
In-Reply-To: <834lvvbzdr.fsf@gnu.org> (Eli Zaretskii's message of "Sun, 04 Jun 2017 17:28:48 +0300")

[-- Attachment #1: Type: text/plain, Size: 2101 bytes --]

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Alex <agrambot@gmail.com>
>> Cc: rudalics@gmx.at,  23568@debbugs.gnu.org
>> Date: Sat, 03 Jun 2017 13:19:50 -0600

>> I changed all instances of `display' to `frame'. I'm still not sure on
>> the name, though. Perhaps a better prefix would be `physical-monitor-*',
>> or just `monitor-*'?
>> 
>> What do you think?
>
> LGTM, thanks.

Just to be clear: you prefer the frame-* prefix?

>> The xmenu.c part contains the actual bugfix, while the frame.el part is
>> just a partial abstraction for the fix so that other procedures can use
>> it (such as compute_tip_xy in xfns.c).
>> 
>> The procedure I'm editing (menu_position_func) is inside an #ifdef
>> USE_GTK, and uses x_display_pixel_{height,width} currently. I'm not sure
>> if it's useful outside of X currently.
>> 
>> I haven't tested the issue on other systems, but I don't think it's an
>> issue in Windows as half of the code came from Windows code according to
>> https://debbugs.gnu.org/cgi/bugreport.cgi?bug=22549#61.
>
> Ah, okay.  Then maybe just add a comment there saying that the w32
> case is handled elsewhere, perhaps with a pointer to the function
> which does that.

Actually, it turns out that I used the half of the code that *didn't*
come from compute_tip_xy from w32fns.c. I instead put a reference to
commit c77ffc8019 in date form in the commit message.

I tested it out on a Windows partition and indeed the problem doesn't
appear there.

>> Here's an updated diff:
>
> Thanks, it would be nice if you could also provide a ChangeLog-style
> commit log message.  That'd make the job of pushing the changes much
> easier.

Right, I just figure that it's easier to save that work until the end
until most/all of the design considerations are taken care of.

Also, I switched from using the geometry attribute (like c77ffc8019
used) to the workarea attribute as according to the GDK doc[1]:

  The work area should be considered when positioning menus and similar
  popups, to avoid placing them below panels, docks or other desktop
  components.

I've attached a patch below.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: multi-monitor-patch --]
[-- Type: text/x-diff, Size: 6701 bytes --]

From 82301e82288298436a568c4968d734df8e77c4d2 Mon Sep 17 00:00:00 2001
From: Alexander Gramiak <agrambot@gmail.com>
Date: Mon, 5 Jun 2017 00:55:56 -0600
Subject: [PATCH] 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 02871e0551..d0ac29694c 100644
--- a/lisp/frame.el
+++ b/lisp/frame.el
@@ -1498,6 +1498,75 @@ frame-monitor-attributes
 	   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 the ATTRIBUTE of the current monitor.
+If FRAME is omitted or nil, use currently selected frame.
+
+By default, the current monitor is said to be the physical
+monitor dominating 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
+current 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 the current 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 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
+current 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 the current 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 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
+current 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 2805249164..6c8a0c506c 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"),
-- 
2.11.0


[-- Attachment #3: Type: text/plain, Size: 106 bytes --]


Footnotes: 
[1]  https://developer.gnome.org/gdk3/stable/GdkScreen.html#gdk-screen-get-monitor-workarea


  reply	other threads:[~2017-06-05  8:03 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-05-17 20:44 bug#23568: 25.0.94; Mode line menus appear incorrectly in some monitor configurations Alex
2016-05-18 21:28 ` Alex
2017-05-28  7:09 ` Alex
2017-05-31  7:14   ` Alex
2017-05-31  9:26     ` martin rudalics
2017-05-31 21:18       ` Alex
2017-06-01  5:39         ` martin rudalics
2017-06-01 20:33           ` Alex
2017-06-01 20:51             ` Alex
2017-06-02  6:10               ` martin rudalics
2017-06-02  7:18                 ` Alex
2017-06-02  9:00                   ` martin rudalics
2017-06-03  0:54                     ` Alex
2017-06-03  6:32                       ` Eli Zaretskii
2017-06-03 19:19                         ` Alex
2017-06-04 14:28                           ` Eli Zaretskii
2017-06-05  8:03                             ` Alex [this message]
2017-06-05 15:36                               ` Eli Zaretskii
2017-06-05 19:33                                 ` martin rudalics
2017-06-10  9:30                               ` Eli Zaretskii

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87vaoauahq.fsf@gmail.com \
    --to=agrambot@gmail.com \
    --cc=23568@debbugs.gnu.org \
    --cc=eliz@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).