Thread from comp.lang.tcl (no replies yet)

Major update to the macOS port of Tk
Posted by steve@digitalsmarties.com (stevel) 3 months ago

Posted on behalf of Marc Culler, there is a major update to the macOS
port of Tk available for testing.

---

Tk Timeline watchers may have noticed a long chain of commits to the
cgimage_with_crossing branch over the last month.  This announcement is
to explain what has been going on there.  In brief, that branch contains
a major change to how drawing works in the macOS port of Tk.  This
change makes drawing in the macOS port work in a way which is much
closer to how it works in the other platforms, and much closer to what
the generic Tk code expects. The result is better performance and
increased stability.

The work is based on an idea of Christopher Chavez's.  He realized that
Apple's Appkit design allowed for an alternative drawing strategy, and
wrote a proof-of-concept implementation.  I had created a branch
containing his implementation a couple of years ago.  Recently I (Marc)
merged that branch with Tk 9 and then spent a few weeks ironing out
wrinkles.  Nicolas Bats, Torsten Berg, Steve Landers, and Kevin Walzer
kindly offered to help me by testing the new implementation on their
apps, some of which provide very challenging environments for Tk.  The
result of this work is in the cgimage-with-crossing branch.  (The branch
also includes a fix for ticket [22349fc78a] which is about <Enter> and
<Leave> events - hence the reference to crossing in the branch tag.)

The five of us are confident that this is a significant improvement.
Some highlights are:
    * Better performance - CPU-intensive apps use about 20% fewer CPU
cycles
    * Better conformance - Most runs of the regression test suite on
macOS 14 show no failures.  This is not close to being the case for the
trunk branch.
    * Fewer graphical artifacts (and work continues on those that
remain.)
    * Fewer crashes (none that we know of at the moment.)

Given that the branch has been tested on large and complex apps - all of
which are working well - this is a major step forwards and we’d like to
see it tested more widely.  To that end, although we are late in the
release cycle we are comfortable recommending that this be merged into
trunk and made part of beta3.  That’s the only way we can guarantee
wider testing. Of course, if any issues crop up they will be addressed
in a timely manner.  If you are a macOS tkchat user please download and
try https://www.codebykevin.com/TkChat_Setup_1.5.2.dmg   If you want to
try the new macOS Tk with your own code and are comfortable building it
yourself please checkout and build the Tk cgimage_with_crossing branch

The rest of this message gives some more details about the changes.

The main difference is that the new drawing strategy does away with the
asynchronous drawing via the drawRect method.  A macOS app which uses
drawRect is supposed to do all of its drawing within the drawRect
method, which can only be called by the NSApplication object.  The app
can request that drawRect be called by setting the viewNeedsDisplay
flag, but can not call drawRect.  The original design of the macOS Aqua
port only partially complied with this strategy.  In fact it was
possible to obtain a valid graphics context for drawing to the backing
layer of a window's contentView even outside of the drawRect method. 
The original port would do this, e.g. in a widget display proc which is
being run as an idle task.  But it would also draw in the drawRect
method by first generating expose events for all widgets that need
updates, then processing those expose events to create idle tasks, then
processing all of those idle tasks immediately.

Obviously this scheme was not what Tk expected.  Nor was it what Apple
expected.  And Apple put an abrupqt end to half of it with the release
of macOS Mojave, which made it impossible to obtain a valid graphics
context outside of the drawRect method. When Tk was first built on
Mojave, it produced nothing but totally black windows.  Eventually this
was worked around by arranging that any drawing operation which failed
due to an invalid graphics context would be rescheduled and also to
modify drawRect to make it (attempt to) run all of those rescheduled
idle tasks.

With the new macOS code it is once again true that Tk always has a valid
graphics context available and hence can draw at any time.  It draws
into a CGBitmapImageContext which serves as the backing store for a Tk
Window.  Instead of calling drawRect, it is configured with Apple's
other option which is to call updateLayer instead.  In the new setup
updateLayer installs the CGImage as the CALayer of the ContentView of
the NSWindow which hosts the Tk toplevel, causing the contents of the
image to appear in the window.  It also creates a new CGImage containing
a copy of the window in which subsequent drawing operations take place. 
This allows drawing operations to take place at the expected time and in
the expected order.  That makes for better stability and less
unpredictability.  In addition, it removes the overhead that arises from
attempting a drawing operation, having it fail due to an invalid
graphics context, rescheduling and rerunning the operation.  This
accounts for the reduced CPU usage.

- Marc

Click on article to view all threads in comp.lang.tcl