This is a problem with Emacs on virtually every GUI platform. Emacs insists that it owns the main loop, while most GUI frameworks insist that they own the main loop. Emacs wants to slurp some events from a queue-like thing, throw some drawing at another queue-like thing, then wait for another event. The GUI instead wants to call back into Emacs whenever an event comes in.

All that being said, Emacs has always worked pretty well for me on Mac. I use Emacs and PDFgrep to spelunk through multi-GBs of PDFs and it is faster than almost anything else.

As Daniel Colascione ('quotemstr' around these parts) said [1]:

> GNU Emacs is an old-school C program emulating a 1980s Symbolics Lisp Machine emulating an old-fashioned Motif-style Xt toolkit emulating a 1970s text terminal emulating a 1960s teletype. Compiling Emacs is a challenge. Adding modern rendering features to the redisplay engine is a miracle.

Emacs owns its main loop because, damnit, it created one before it was cool.

Hats off to any heroes who would manage to drag it, kicking and screaming, into this millennium.

[1] https://gist.github.com/ghosty141/c93f21d6cd476417d4a9814eb7...

I'm not sure if I understood the issue right, but if the issue is with the runloop, I think you can technically reimplement it yourself with nextEventMatchingMask.

But their design just seems broken, if they're re-initializing the graphics context on every runloop iteration?

That’s what nsterm.m uses. As far as I can tell, the problem is that nextEventMatchingMask needs to be called from the NSApp main loop. But calling NSApplication:run blocks and takes over the main loop, which doesn’t work with for Emacs.

So what Emacs does is keep control of its own main loop. It has a select() that listens for events, and then calls NSApplication:run in the event handler. Emacs’s implementation of run() processes all pending events and then exits and returns control to the real Emacs main loop. So every keystroke or timer event in Emacs invokes setting up and tearing down the entire NSApplication main loop.

The relevant code is ns_select_1 in nsterm.m, line 5102. https://github.com/emacs-mirror/emacs/blob/master/src/nsterm...

Hm I'm still not sure I understand what you mean by "sets up and tears down the entire main loop". Even in normal cocoa applications every keystroke or input event causes an event to be dispatched from WindowServer to the application which gets handled by the runloop. The expectation of course is that you never do any blocking work on this runloop. nextEventMatchingMask doesn't have to block, you can have it return if there are no events that need to be processed so it can integrate with your own runloop (of course bypassing [NSApplication run] and implementing the runloop yourself this way is discouraged, but I don't think it's forbidden)

The model of "slurp some events from a queue-like thing, throw some drawing at another queue-like thing, then wait for another event" is precisely what [NSApplication run] already implements. Per Apple

>A Cocoa application is event driven: It fetches an event from the queue, dispatches it to an appropriate object, and, after the event is handled, fetches the next event. With some exceptions (such as modal event loops) an application continues in this pattern until the user quits it.

Yeah I can't see it tearing down the message pipe, that would exit the app.

But what it is doing is first posting a message that will call `[NSApp stop]` then calling `[NSApp run]`. Which is odd, because `[NSApp nextEvent]` exists, but even if that doesn't work it's not the way I'd do it. However I'm sure there are very good reasons to do it this way when you know the emacs source.

With an old and conservative project like Emacs, the reason could just as likely be that it was done that way for the NEXTSTEP port in the early 90s and that nobody revisited it since then. Oftentimes the reason is just legacy, limited manpower and "don't fix what ain't broken".

[deleted]