Repository navigation
Add the tray's macOS head - #991
Merged
Merged
Conversation
src/DiffEngineTray.Mac is the tray on macOS: the core's tracker and inline queue, with a status bar item from a native library of its own, diffengine_tray (decision G), so a tray change never moves the viewer's ABI version. It is PlatformHeadsMac's StatusItem behind native/include/detray.h, a second product of native/swift, which build-native collects into the head's runtimes through PlatformHeads 0.8.0's mac-libraries. The menu crosses the ABI as one array, each item with its parent by index, and the Swift side makes it a tree again. The managed half, Detray and MacTray, is in the core, as ProcessEx's native calls are, so MacTrayTests hands the library a menu and reads back what it rebuilt on the macOS job. AppKit is driven from the main thread only, so the head runs TrayLoop.RunPumped on the thread Main starts on, with the library's pump as its wait. The app is an accessory with no Dock icon, App Nap is off, and a logout is TerminateGuard's, ending the loop as Exit does. The pictures and the login item move to the core for both heads off Windows. Every PlatformHeads pin moves to 0.8.0.
Co-authored-by: SimonCropp <122666+SimonCropp@users.noreply.github.com>
The macOS jobs treat the tray's dylib as they treat the viewer's. The unix job copies the freshly built libdiffengine_tray.dylib into the head's runtimes before testing, so MacTrayTests hands its menu to the Swift as it is now, not as it was at the last build-native rebuild. The native job checks both committed dylibs for both slices, and on osx-arm64 runs DiffEngineTray.Core.Tests against the committed one.
This was referenced Oct 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of phase 3 of the cross-platform tray plan: the tray on macOS. Not packed yet; how the heads ship is phase 4.
The head.
src/DiffEngineTray.Macis anExenamedDiffEngineTray, as the Windows and Linux heads are. It is the Linux head step for step, with a status bar item in place of the StatusNotifierItem: the core's tracker and inline queue, the same menu (Exit, "Run at startup", Debug view, Open logs, Raise issue), and warnings through macOS's notifications. "Run at startup" is a launch agent, through PlatformHeads'RunAtLogin.ForPlatform. Built inRelease-NotWindowsonly.The library (decision G). The status bar item is PlatformHeadsMac's
StatusItemin a native library of its own,diffengine_tray, so a tray change never moves the viewer's ABI version. It is a second product ofnative/swift(Sources/Detray, with aCDetraybridge to the header), and PlatformHeads 0.8.0'smac-librarieshasbuild-nativecollect it intosrc/DiffEngineTray.Mac/runtimesin the same run as the viewer's. The binaries came in through #990.The ABI.
native/include/detray.h,DETRAY_VERSION1, which the head checks before anything else. A C struct cannot hold a list of itself, so the menu goes over as one array ofDetrayItems, each with its parent's index (-1 for the top level), and the Swift side makes it a tree again. Strings are UTF-8, allocated and freed by the managed side around each call. The icons and glyphs are handed over once at start, as PNGs.detray_pollreturns the chosen items in the order they were chosen.detray_describewrites the menu as the library rebuilt it, for the tests. The managed half (Detray,DetrayItem,MacTray) is in the core, as ProcessEx's native calls are, so the core's tests can reach it.The main thread. AppKit is driven from the main thread only, so the head's
Mainis synchronous, and the loop is the core's newTrayLoop.RunPumped, on the threadMainstarts on, with the library's pump as its wait (a tenth of a second at most, less once there is input). Nothing inProgram.Innerawaits before the loop returns.TrayLoop.Runis unchanged, and still what the Linux head uses.The app. An accessory, with no Dock icon and no menu bar of its own. App Nap is off (
NapGuard), as the viewer has it, or the sockets and scan are answered late. A logout reaches an app with no Dock icon as a quit, whichTerminateGuardrefuses and reports, and the loop then ends the way Exit does, staging the queue on the way out. SIGTERM and SIGHUP areSessionEndSignals', as on Linux.No status bar. With no library for the machine, one of another version, or no button from AppKit, the head runs as Linux does with no tray host: it holds the queue, tracks what test runs send it, and says once, in a notification, why there is no icon.
Shared now. The pictures move to the core as
TrayPictures(the Linux head'sTrayImages, now with the PNGs the Mac library takes), embedded in the core from the Windows head's files, andTrayLoginmoves to the core.PlatformHeads. All four packages and the native pins (
native/CMakeLists.txt,build-native.yml) move to 0.8.0. The viewer's dylib was rebuilt against it in #990; its Linux binaries came out the same.CI. The macOS jobs treat the tray's dylib as they treat the viewer's. The
unixjob builds it from source before testing, so the tests use the Swift as it is now. Thenativejob checks both committed dylibs have both slices, and on osx-arm64 runsDiffEngineTray.Core.Testsagainst the committed one.Tests:
MacTrayTests.EachItemFollowsTheItemWhoseSubmenuItIsIn: the flattened menu, in order, over a menu with a glyph, a toggle each way, a separator, a disabled heading, a submenu in a submenu and text beyond ASCII.MacTrayTests.TheLibraryRebuildsTheMenuItWasHanded, macOS only: the library's version, then that menu handed over and read back withdetray_describe, every field of every item. It needs no status bar, so it runs on CI. This PR's macOS jobs are the first time it runs.TrayLoopTests.APumpedLoopStaysOnItsThread: on a thread of its own, the pump and a chosen item both run on the thread that started the loop, the menu is applied, and Exit ends it.