Subject: Re: bug#76691: `display-monitor-attributes-list` not working properly on macOS

From: Ship Mints <[email-masked]>
Date: Wed, 5 Mar 2025 10:40:11 -0500

On Wed, Mar 5, 2025 at 8:44 AM Robert Pluim <[email-masked]> wrote:

> >>>>> On Wed, 5 Mar 2025 06:38:05 -0500, Ship Mints <[email-masked]>
> said:
>
>     Ship> On Wed, Mar 5, 2025 at 4:04 AM Robert Pluim <[email-masked]>
> wrote:
>     >> >>>>> On Tue, 4 Mar 2025 11:06:29 -0500, Ship Mints <
> [email-masked]>
>     >> said:
>     >>
>     Ship> On Tue, Mar 4, 2025 at 10:59 AM Ship Mints <[email-masked]>
>     >> wrote:
>     >> >> On Tue, Mar 4, 2025 at 10:34 AM Robert Pluim <[email-masked]>
>     >> wrote:
>     >> >>
>     >> >>> It gives me reasonable looking names here:
>     >> >>>
>     >> >>> (((name . "PL3467WQ") (geometry 0 0 3440 1440) (workarea 0 25
> 3440
>     >> >>> 1415) (mm-size 801 329) (frames #<frame *scratch* 0x131887028>)
>     >> >>> (source . "NS")) ((name . "Built-in Display") (geometry 459 1440
>     >> 2048
>     >> >>> 1332) (workarea 459 1440 2048 1287) (mm-size 290 189) (frames)
>     >> (source
>     >> >>> . "NS")))
>     >> >>>
>     >> >>
>     >> >> Robert,
>     >> >>
>     >> >> When you run (x-display-list), what do you get? Just your host
> name,
>     >> >> right? And if you unplug and replug your monitors and rerun?
>     >> >>
>     >>
>     >> I get just my host name.
>     >>
>     Ship> And also (display-monitor-attributes-list) just to make sure it
>     >> works with
>     Ship> comings and goings of displays.
>     >>
>     >> I unplugged and replugged my external monitor, and Emacs crashed in
>     >> `read_char', which is a different crash from the one I saw earlier,
>     >> which was in `Fx_create_frame'. Iʼve got the lldb session if it
> helps.
>     >>
>
>     Ship> The way make-frame-on-monitor is implemented is nothing
> special.  Just find
>     Ship> the coordinates of the "workspace" occupied by the named monitor
> and use
>     Ship> those as the basis for the new frame.  The bt from yesterday's
> perhaps
>     Ship> indicates some kind of guard might be needed for frame
> coordinates that
>     Ship> might be out of bounds, perhaps?  Do you think the frame on the
> second
>     Ship> monitor was larger than the screen?  I admit to not having
> played much with
>     Ship> trying to make oversized frames but it happens to me
> occasionally if only
>     Ship> over left and right by a column or row or two, not more.
>
> The default frame size is smaller than both monitors. I guess itʼs
> possible something decided to place it off screen.
>
>     Ship> I'm curious what the read_char bt looks like.  You saw the
> patch, it's
>     Ship> pretty much a nothing.  I updated it yesterday to use xstrdup
> instead of
>     Ship> the two-step.  I doubt that's anything.  But here's the updated
> patch, just
>     Ship> in case.
>
> It might be an existing emacs issue, rather than anything to do with
> your patch (if youʼre feeling inspired, rewriting the socket handling
> on macOS to use the normal event loop rather than the hackery with a
> separate thread to run select might improve the port a lot. Or it might
> make no difference)
>
> Anyway, Iʼm suspicious about the fact that `ns_read_socket' appears in
> this backtrace twice.
>
> * thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
>   * frame #0: 0x00000001854e3720 libsystem_kernel.dylib`__pthread_kill + 8
>     frame #1: 0x000000018551bf70 libsystem_pthread.dylib`pthread_kill + 288
>     frame #2: 0x0000000185428908 libsystem_c.dylib`abort + 128
>     frame #3: 0x0000000185331e38 libsystem_malloc.dylib`malloc_vreport +
> 896
>     frame #4: 0x000000018535a458 libsystem_malloc.dylib`malloc_zone_error
> + 100
>     frame #5: 0x0000000185349774
> libsystem_malloc.dylib`nanov2_guard_corruption_detected + 44
>     frame #6: 0x0000000185349734
> libsystem_malloc.dylib`nanov2_allocate_outlined + 460
>     frame #7: 0x0000000185348468 libsystem_malloc.dylib`nanov2_calloc_type
> + 568
>     frame #8: 0x000000018b69c610 CoreGraphics`CGGStackCreateWithGState + 40
>     frame #9: 0x000000018b71b0b0
> CoreGraphics`CGDisplayListDrawInContextDelegate + 596
>     frame #10: 0x0000000189590ee4 AppKit`___lldb_unnamed_symbol169773 + 884
>     frame #11: 0x0000000189609008 AppKit`___lldb_unnamed_symbol172077 + 100
>     frame #12: 0x000000018e165fbc QuartzCore`CABackingStoreUpdate_ + 284
>     frame #13: 0x000000018e1bc2d8 QuartzCore`invocation function for block
> in CA::Layer::display_() + 120
>     frame #14: 0x000000018e16503c QuartzCore`-[CALayer _display] + 1636
>     frame #15: 0x0000000189608e94 AppKit`___lldb_unnamed_symbol172075 +
> 1372
>     frame #16: 0x00000001896096b0 AppKit`___lldb_unnamed_symbol172087 + 28
>     frame #17: 0x00000001894fc49c AppKit`___lldb_unnamed_symbol166799 + 148
>     frame #18: 0x0000000189608f88 AppKit`___lldb_unnamed_symbol172076 + 128
>     frame #19: 0x000000018e1641b8
> QuartzCore`CA::Layer::display_if_needed(CA::Transaction*) + 784
>     frame #20: 0x000000018e2f30e4
> QuartzCore`CA::Context::commit_transaction(CA::Transaction*, double,
> double*) + 528
>     frame #21: 0x000000018e146780 QuartzCore`CA::Transaction::commit() +
> 648
>     frame #22: 0x000000018929da9c
> AppKit`__62+[CATransaction(NSCATransaction)
> NS_setFlushesWithDisplayLink]_block_invoke + 272
>     frame #23: 0x0000000189ca88f4
> AppKit`___NSRunLoopObserverCreateWithHandler_block_invoke + 64
>     frame #24: 0x0000000185603be8
> CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__
> + 36
>     frame #25: 0x0000000185603ad4 CoreFoundation`__CFRunLoopDoObservers +
> 552
>     frame #26: 0x0000000185603104 CoreFoundation`__CFRunLoopRun + 788
>     frame #27: 0x0000000185602734 CoreFoundation`CFRunLoopRunSpecific + 588
>     frame #28: 0x0000000190b71530 HIToolbox`RunCurrentEventLoopInMode + 292
>     frame #29: 0x0000000190b7717c HIToolbox`ReceiveNextEventCommon + 216
>     frame #30: 0x0000000190b77508
> HIToolbox`_BlockUntilNextEventMatchingListInModeWithFilter + 76
>     frame #31: 0x000000018917a848 AppKit`_DPSNextEvent + 660
>     frame #32: 0x0000000189ae0c24 AppKit`-[NSApplication(NSEventRouting)
> _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 688
>     frame #33: 0x000000018916d874 AppKit`-[NSApplication run] + 480
>     frame #34: 0x00000001001fbf44 emacs`-[EmacsApp
> run](self=0x0000000129722680, _cmd=<unavailable>) at nsterm.m:5938:7 [opt]
>     frame #35: 0x000000010020efb0
> emacs`ns_read_socket_1(terminal=<unavailable>, hold_quit=<unavailable>,
> no_release=<unavailable>) at nsterm.m:4812:11 [opt]
>     frame #36: 0x00000001000dc838 emacs`gobble_input at keyboard.c:7919:17
> [opt]
>     frame #37: 0x00000001000d8d80 emacs`swallow_events [inlined]
> get_input_pending(flags=1) at keyboard.c:7875:7 [opt]
>     frame #38: 0x00000001000d8d0c emacs`swallow_events(do_display=true) at
> keyboard.c:4602:3 [opt]
>     frame #39: 0x0000000100007d38 emacs`sit_for(timeout=(EMACS_INT) $4 =
> 30, reading=true, display_option=1) at dispnew.c:6284:3 [opt]
>     frame #40: 0x00000001000d5ba0
> emacs`read_char(commandflag=<unavailable>, map=<unavailable>,
> prev_event=<unavailable>, used_mouse_menu=<unavailable>,
> end_time=<unavailable>) at keyboard.c:2923:11 [opt]
>     frame #41: 0x00000001000d2ab0
> emacs`read_key_sequence(keybuf=<unavailable>, prompt=(struct Lisp_Symbol *)
> $22 = 0x00000001008eefe0, dont_downcase_last=false,
> can_return_switch_frame=true, fix_current_buffer=true,
> prevent_redisplay=<unavailable>, disable_text_conversion_p=<unavailable>)
> at keyboard.c:10743:12 [opt]
>     frame #42: 0x00000001000d0ee4 emacs`command_loop_1 at
> keyboard.c:1429:15 [opt]
>     frame #14: 0x000000018e16503c QuartzCore`-[CALayer _display] + 1636
>     frame #15: 0x0000000189608e94 AppKit`___lldb_unnamed_symbol172075 +
> 1372
>     frame #16: 0x00000001896096b0 AppKit`___lldb_unnamed_symbol172087 + 28
>     frame #17: 0x00000001894fc49c AppKit`___lldb_unnamed_symbol166799 + 148
>     frame #18: 0x0000000189608f88 AppKit`___lldb_unnamed_symbol172076 + 128
>     frame #19: 0x000000018e1641b8
> QuartzCore`CA::Layer::display_if_needed(CA::Transaction*) + 784
>     frame #20: 0x000000018e2f30e4
> QuartzCore`CA::Context::commit_transaction(CA::Transaction*, double,
> double*) + 528
>     frame #21: 0x000000018e146780 QuartzCore`CA::Transaction::commit() +
> 648
>     frame #22: 0x000000018929da9c
> AppKit`__62+[CATransaction(NSCATransaction)
> NS_setFlushesWithDisplayLink]_block_invoke + 272
>     frame #23: 0x0000000189ca88f4
> AppKit`___NSRunLoopObserverCreateWithHandler_block_invoke + 64
>     frame #24: 0x0000000185603be8
> CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__
> + 36
>     frame #25: 0x0000000185603ad4 CoreFoundation`__CFRunLoopDoObservers +
> 552
>     frame #26: 0x0000000185603104 CoreFoundation`__CFRunLoopRun + 788
>     frame #27: 0x0000000185602734 CoreFoundation`CFRunLoopRunSpecific + 588
>     frame #28: 0x0000000190b71530 HIToolbox`RunCurrentEventLoopInMode + 292
>     frame #29: 0x0000000190b7717c HIToolbox`ReceiveNextEventCommon + 216
>     frame #30: 0x0000000190b77508
> HIToolbox`_BlockUntilNextEventMatchingListInModeWithFilter + 76
>     frame #31: 0x000000018917a848 AppKit`_DPSNextEvent + 660
>     frame #32: 0x0000000189ae0c24 AppKit`-[NSApplication(NSEventRouting)
> _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 688
>     frame #33: 0x000000018916d874 AppKit`-[NSApplication run] + 480
>     frame #34: 0x00000001001fbf44 emacs`-[EmacsApp
> run](self=0x0000000129722680, _cmd=<unavailable>) at nsterm.m:5938:7 [opt]
>     frame #35: 0x000000010020efb0
> emacs`ns_read_socket_1(terminal=<unavailable>, hold_quit=<unavailable>,
> no_release=<unavailable>) at nsterm.m:4812:11 [opt]
>     frame #36: 0x00000001000dc838 emacs`gobble_input at keyboard.c:7919:17
> [opt]
>     frame #37: 0x00000001000d8d80 emacs`swallow_events [inlined]
> get_input_pending(flags=1) at keyboard.c:7875:7 [opt]
>     frame #38: 0x00000001000d8d0c emacs`swallow_events(do_display=true) at
> keyboard.c:4602:3 [opt]
>     frame #39: 0x0000000100007d38 emacs`sit_for(timeout=(EMACS_INT) $4 =
> 30, reading=true, display_option=1) at dispnew.c:6284:3 [opt]
>     frame #40: 0x00000001000d5ba0
> emacs`read_char(commandflag=<unavailable>, map=<unavailable>,
> prev_event=<unavailable>, used_mouse_menu=<unavailable>,
> end_time=<unavailable>) at keyboard.c:2923:11 [opt]
>     frame #41: 0x00000001000d2ab0
> emacs`read_key_sequence(keybuf=<unavailable>, prompt=(struct Lisp_Symbol *)
> $22 = 0x00000001008eefe0, dont_downcase_last=false,
> can_return_switch_frame=true, fix_current_buffer=true,
> prevent_redisplay=<unavailable>, disable_text_conversion_p=<unavailable>)
> at keyboard.c:10743:12 [opt]
>     frame #42: 0x00000001000d0ee4 emacs`command_loop_1 at
> keyboard.c:1429:15 [opt]
>     frame #43: 0x000000010015b994
> emacs`internal_condition_case(bfun=(emacs`command_loop_1 at
> keyboard.c:1324), handlers=<unavailable>, hfun=(emacs`cmd_error at
> keyboard.c:970)) at eval.c:1613:25 [opt]
>     frame #44: 0x00000001000d0b78 emacs`command_loop_2(handlers=(struct
> Lisp_Symbol *) $43 = 0x00000001008ef070) at keyboard.c:1168:11 [opt]
>     frame #45: 0x000000010015b030 emacs`internal_catch(tag=(struct
> Lisp_Symbol *) $64 = 0x00000001008f63f0, func=(emacs`command_loop_2 at
> keyboard.c:1164), arg=(struct Lisp_Symbol *) $85 = 0x00000001008ef070) at
> eval.c:1292:25 [opt]
>     frame #46: 0x00000001000d03a8 emacs`command_loop at keyboard.c:1138:13
> [opt]
>     frame #47: 0x00000001000d0270 emacs`recursive_edit_1 at
> keyboard.c:754:9 [opt]
>     frame #48: 0x00000001001068c0 emacs`Fread_from_minibuffer [inlined]
> read_minibuf(map=<unavailable>, initial=<unavailable>, prompt=(struct
> Lisp_String *) $94 = 0x00000001297199a0, expflag=<unavailable>,
> histvar=<unavailable>, histpos=(EMACS_INT) $100 = 0, defalt=<unavailable>,
> allow_props=<unavailable>, inherit_input_method=<unavailable>) at
> minibuf.c:905:3 [opt]
>     frame #49: 0x0000000100105bf0
> emacs`Fread_from_minibuffer(prompt=<unavailable>,
> initial_contents=<unavailable>, keymap=(struct Lisp_Cons *) $106 =
> 0x0000000102fe8910, read=<unavailable>, hist=<unavailable>,
> default_value=<unavailable>, inherit_input_method=(struct Lisp_Symbol *)
> $124 = 0x00000001008eefe0) at minibuf.c:1394:9 [opt]
>     frame #50: 0x00000001001a54f8 emacs`exec_byte_code(fun=<unavailable>,
> args_template=<unavailable>, nargs=<unavailable>, args=<unavailable>) at
> bytecode.c:812:14 [opt]
>     frame #51: 0x000000010015e9f0 emacs`funcall_lambda(fun=<unavailable>,
> nargs=<unavailable>, arg_vector=<unavailable>) at eval.c:3252:9 [opt]
> [artificial]
>     frame #52: 0x000000010015e298 emacs`funcall_general(fun=<unavailable>,
> numargs=<unavailable>, args=<unavailable>) at lisp.h:0:43 [opt] [artificial]
>     frame #53: 0x0000000100159260 emacs`Ffuncall(nargs=9, args=(struct
> Lisp_Symbol *) $133 = 0x00000002706ed540) at eval.c:3093:21 [opt]
>     frame #54: 0x0000000100106f94 emacs`Fcompleting_read(prompt=(struct
> Lisp_String *) $139 = 0x00000001297199a0, collection=(struct Lisp_Vector *)
> $145 = 0x000000010285dd38, predicate=(struct Lisp_Vector *) $151 =
> 0x000000012a1d5748, require_match=(struct Lisp_Symbol *) $169 =
> 0x00000001008ef010, initial_input=(struct Lisp_Symbol *) $190 =
> 0x00000001008eefe0, hist=(struct Lisp_Symbol *) $211 = 0x000000010285c5c8,
> def=(struct Lisp_Symbol *) $232 = 0x00000001008eefe0,
> inherit_input_method=(struct Lisp_Symbol *) $253 = 0x00000001008eefe0) at
> minibuf.c:2049:10 [opt]
>     frame #55: 0x00000001001a54f8 emacs`exec_byte_code(fun=<unavailable>,
> args_template=<unavailable>, nargs=<unavailable>, args=<unavailable>) at
> bytecode.c:812:14 [opt]
>     frame #56: 0x00000001001a4ad4 emacs`Fbyte_code(bytestr=<unavailable>,
> vector=(struct Lisp_Vector *) $262 = 0x000000010285c3d8,
> maxdepth=(EMACS_INT) $268 = 3) at bytecode.c:329:10 [opt]
>     frame #57: 0x00000001001588c0 emacs`eval_sub(form=(struct Lisp_Cons *)
> $274 = 0x000000010285c3a8) at eval.c:2604:15 [opt]
>     frame #58: 0x000000010015ce4c emacs`Feval(form=<unavailable>,
> lexical=<unavailable>) at eval.c:2462:28 [opt]
>     frame #59: 0x0000000100155f48
> emacs`Fcall_interactively(function=<unavailable>, record_flag=(struct
> Lisp_Symbol *) $292 = 0x00000001008eefe0, keys=(struct Lisp_Vector *) $301
> = 0x000000012a2505c0) at callint.c:325:15 [opt]
>     frame #60: 0x00000001001a54f8 emacs`exec_byte_code(fun=<unavailable>,
> args_template=<unavailable>, nargs=<unavailable>, args=<unavailable>) at
> bytecode.c:812:14 [opt]
>     frame #61: 0x000000010015e9f0 emacs`funcall_lambda(fun=<unavailable>,
> nargs=<unavailable>, arg_vector=<unavailable>) at eval.c:3252:9 [opt]
> [artificial]
>     frame #62: 0x000000010015e298 emacs`funcall_general(fun=<unavailable>,
> numargs=<unavailable>, args=<unavailable>) at lisp.h:0:43 [opt] [artificial]
>     frame #63: 0x0000000100159260 emacs`Ffuncall(nargs=2, args=(struct
> Lisp_Symbol *) $319 = 0x00000002706edbc0) at eval.c:3093:21 [opt]
>     frame #64: 0x00000001000d10e4 emacs`command_loop_1 at
> keyboard.c:1550:13 [opt]
>     frame #65: 0x000000010015b994
> emacs`internal_condition_case(bfun=(emacs`command_loop_1 at
> keyboard.c:1324), handlers=<unavailable>, hfun=(emacs`cmd_error at
> keyboard.c:970)) at eval.c:1613:25 [opt]
>     frame #66: 0x00000001000d0b78 emacs`command_loop_2(handlers=(struct
> Lisp_Symbol *) $340 = 0x00000001008ef070) at keyboard.c:1168:11 [opt]
>     frame #67: 0x000000010015b030 emacs`internal_catch(tag=<unavailable>,
> func=(emacs`command_loop_2 at keyboard.c:1164), arg=(struct Lisp_Symbol *)
> $361 = 0x00000001008ef070) at eval.c:1292:25 [opt]
>     frame #68: 0x000000010023ee7c emacs`command_loop.cold.1 at
> keyboard.c:1146:2 [opt]
>     frame #69: 0x00000001000d03c0 emacs`command_loop at keyboard.c:1145:2
> [opt]
>     frame #70: 0x00000001000d0270 emacs`recursive_edit_1 at
> keyboard.c:754:9 [opt]
>     frame #71: 0x00000001000d0550 emacs`Frecursive_edit at
> keyboard.c:837:3 [opt]
>     frame #72: 0x00000001000cf300 emacs`main(argc=<unavailable>,
> argv=0x000000016fdff2c8) at emacs.c:2646:3 [opt]
>     frame #73: 0x000000018519c274 dyld`start + 2840
> (lldb)
>

Hmm.  I never see these.  I wonder what's different about your set up.
These seem independent of the NS display name improvement.

Should we push the display name patch and see what feedback we get from a
larger audience?  It seems low risk vs. rewriting macOS socket handling.