Subject: bug#76051: 29.4; Incorrect workarea entry in display-monitor-attributes-list on macOS

From: Alan Third <[email-masked]>
Date: Mon, 26 May 2025 20:34:25 +0100

On Tue, Feb 04, 2025 at 02:42:07PM +0100, Anders Lindgren wrote:
> Hi!

Hi Anders,

> I think the `display-monitor-attributes-list' function returns an incorrect
> `workarea' entry on macOS.
> 
> Concretely, I have a laptop with an external monitor connected. The
> external monitor is placed above to the left.
> 
> The `display-monitor-attributes-list' function returns the following
> (abbreviated):
> 
>     (((geometry 0 0 1728 1117) (workarea 0 38 1728 1079) ...
>      ((geometry -2560 -723 2560 1440) (workarea -2560 -638 2560 1330) ...))
> 
> The first member in the list describes the laptop monitor (the primary
> monitor), the second the external monitor.
> 
> The first two numbers, `X' and `Y', in each entry describe the position of
> the top left corner of the area, relative to the top left corner of the
> primary monitor.
> 
> The number that I think is incorrect is the `Y' of the workarea of the
> external monitor, which is "-638". It indicates that there is a (723-638 =
> 85 pixel area on top of the screen which is outside the work area.)
> 
> However, in reality, there is a 25 pixel menu bar which should be outside
> the work area. The 85 probably comes from the macOS dock, which is placed
> in the lower part of the screen.
> 
> The height field correctly includes both the menu bar and the dock.
> 
> I've tested this on Emacs 29.4 and 30.0.93 on macOS 15.1.

I'm pretty sure the arithmetic was wrong, and it was treating external
monitors differently from the primary screen for some reason I
couldn't work out.

Can you check if the attached looks right to you? I can't check as I
don't have an external monitor here.
-- 
Alan Third
>From a5c1edd911322805cf9a2fc9cb08aac98d05c2f6 Mon Sep 17 00:00:00 2001
From: Alan Third <[email-masked]>
Date: Mon, 26 May 2025 20:27:39 +0100
Subject: [PATCH] Fix NS port screen geometry report (bug#76051)

* src/nsfns.m (Fns_display_monitor_attributes_list): Fix the arithmetic
to calculate the origin of the visible frame.
---
 src/nsfns.m | 33 +++++++++++----------------------
 1 file changed, 11 insertions(+), 22 deletions(-)

diff --git a/src/nsfns.m b/src/nsfns.m
index b1ed0eff58a..02dc1ea359c 100644
--- a/src/nsfns.m
+++ b/src/nsfns.m
@@ -2786,41 +2786,30 @@ Frames are listed from topmost (first) to bottommost (last).  */)
       struct MonitorInfo *m = &monitors[i];
       NSRect fr = [s frame];
       NSRect vfr = [s visibleFrame];
-      short y, vy;
 
 #ifdef NS_IMPL_COCOA
       NSDictionary *dict = [s deviceDescription];
       NSNumber *nid = [dict objectForKey:@"NSScreenNumber"];
       CGDirectDisplayID did = [nid unsignedIntValue];
 #endif
+
+      /* The primary display is always the first in the array.  */
       if (i == 0)
-        {
-          primary_display_height = fr.size.height;
-          y = (short) fr.origin.y;
-          vy = (short) vfr.origin.y;
-        }
-      else
-        {
-          /* Flip y coordinate as NS screen coordinates originate from
-	     the bottom.  */
-          y = (short) (primary_display_height - fr.size.height - fr.origin.y);
-          vy = (short) (primary_display_height -
-                        vfr.size.height - vfr.origin.y);
-        }
+	primary_display_height = fr.size.height;
+
+      /* Flip y coordinate as NS screen coordinates originate from
+	 the bottom.  */
 
       m->geom.x = (short) fr.origin.x;
-      m->geom.y = y;
+      m->geom.y = (short) (primary_display_height
+			   - fr.size.height - fr.origin.y);
       m->geom.width = (unsigned short) fr.size.width;
       m->geom.height = (unsigned short) fr.size.height;
 
+      /* The work area excludes the menu bar and the dock.  */
       m->work.x = (short) vfr.origin.x;
-      /* y is flipped on NS, so vy - y are pixels missing at the
-	 bottom, and fr.size.height - vfr.size.height are pixels
-	 missing in total.
-
-	 Pixels missing at top are fr.size.height - vfr.size.height -
-	 vy + y.  work.y is then pixels missing at top + y.  */
-      m->work.y = (short) (fr.size.height - vfr.size.height) - vy + y + y;
+      m->work.y = (short) (primary_display_height -
+			   vfr.size.height - vfr.origin.y);
       m->work.width = (unsigned short) vfr.size.width;
       m->work.height = (unsigned short) vfr.size.height;
 
-- 
2.41.0