Skip to content

Instantly share code, notes, and snippets.

@retroplasma
Last active July 13, 2026 05:34
Show Gist options
  • Select an option

  • Save retroplasma/ec21767d0a8380c7ea9c2fbee1c7d6bf to your computer and use it in GitHub Desktop.

Select an option

Save retroplasma/ec21767d0a8380c7ea9c2fbee1c7d6bf to your computer and use it in GitHub Desktop.
Unlag Neo: Macbook Neo Cursor lag "fix"

Unlag Neo: A "fix" for the cursor lag on Macbook Neo

Macbook Neo (I'm on macOS Tahoe 26.5.2) cursor is lagging when the cursor is near the screen's edges or when it enters a Terminal window. [1][2][3][4][5]

[Click here for more info and background]

Why is it lagging?

I don't know. But at the moment when it lags the system switches from hardware cursor to software cursor (CGCursorIsDrawnInFramebuffer() goes from 0 to 1) so maybe that transition is stalled somehow on Macbook Neo. Maybe on the screen's edges half of the mouse is rendered and in the Terminal there is some other graphics stuff happening, so that's why macOS wants to use SW cursor there, but idk. For some reason, HW cursor is only used after ~17 seconds after the screen was locked. Before that, there is no lag.

Proper fix:

Either fix the lag directly (hope Apple does it) or maybe forcing to always use SW cursor would help as a intermediate step. To force SW cursor directly we could reverse engineer WindowServer but that probably implies turning SIP off and stuff. Or maybe not. And maybe HW/SW cursor is just a correlation. We could also look what happens ~17 seconds after Mac is unlocked.

Other ok "fix" for now:

You can also turn on macOS Color Filter (e.g. Blue/Yellow filter) to low setting, but this changes the colors a bit and doesn't seem to help if Night Shift is on at the same time. Also the mouse cursor size can be changed, but a small change doesn't work; it has to be significantly bigger to trigger the fix. But to keep the colors and cursor ok, we can also do a Screen Recording. However with screen recording there is an indicdator (image) on the top right of the screen. Maybe it can be disabled with SIP off but I haven't looked.

Essentially anything that forces the compositor(?) to do SW cursor (or whatever else) will do. No lag anymore.

Other idea:

Find out what's special about the Terminal window and create an app that does the same thing and overlays its window over everything.

Anyway, I chose Screen Recording.

My favorite "fix" right now: Screen Recording

For me, the Screen Recording solution is the least annoying one. The indicator (image) is small enough and we can turn off screen recording when a fullscreen video is playing (where the indicator would be annoying otherwise imho). Obviously, such a fix would invalidate the purpose of that indicator, but I think there are two kinds of indicators. There is a larger one also, which (I think) app store apps have, so I think we are fine-ish.

The cursor stops lagging.

It doesn't need much CPU/GPU:

Record 1 pixel every 10 seconds (to /dev/null basically, no SSD write)

Ok how to do it:

There is a script (create_unlag_neo_app.sh) below in this Gist. The script creates an .app which can be launched (no need for Dev Account or Xcode etc.).

The script can be used like this (Terminal):

chmod +x create_unlag_neo_app.sh
./create_unlag_neo_app.sh
# -> creates "Unlag Neo.app"

App that the script created:

image

Put Unlag Neo.app somewhere. In /Applications or something. Run it and give it the permission it wants.

In the menu bar (image) you can turn it off and also try the "Pause in Fullscreen" feature if you want, which does an OK job I think to hide the indicator. I tried with VLC and YouTube and a few apps.

image
[[Click here for Debug info]]

Debug

If you want to hack create_unlag_neo_app.sh and/or rebuild the app multiple times, macOS permission settings UI can reset and behave a bit weirdly (probably because there is no code-signing or bundle ID). What helps is to manually remove the old version from all the relevant permission setting UIs and then to add/drag the new app binary there (possibly both "/Applications/Unlag Neo.app" and "/Applications/Unlag Neo.app/Contents/MacOS/Unlag Neo"). You can also try to change "Unlag Neo" to something else in the script. If you need to delete "~/Library/Preferences/Unlag Neo.plist" it might be helpful to run killall cfprefsd after. Or you can probably use Xcode and set it up with the usual workflow there.

#!/bin/bash
set -Efeuxo pipefail
APP="Unlag Neo"
DISPLAY_NAME="Unlag Neo"
if [ -e "${APP}.app" ]; then
echo "Error: ${APP}.app exists already"
exit 1
fi
tmp_dir="$(mktemp -d)"
set +x
final_msg="Error."
set -x
trap 'rm -rf -- "$tmp_dir"&&echo "deleted temp dir: \"$tmp_dir\""&&echo "$final_msg"' EXIT
cd "$tmp_dir"
mkdir -p "${APP}.app/Contents/MacOS" "${APP}.app/Contents/Resources"
PATH="$PATH:/usr/libexec"
swiftc - -o "${APP}.app/Contents/MacOS/${APP}" <<'SWIFT' &
import Foundation
import AppKit
import CoreGraphics
import CoreMedia
import ServiceManagement
import ApplicationServices
@preconcurrency import ScreenCaptureKit
struct CaptureError: Error, CustomStringConvertible {
let description: String
init(_ description: String) { self.description = description }
}
@MainActor var showedPermissionAlert = false
@MainActor var showedAccessibilityAlert = false
@MainActor
func relaunchUnlag() {
let isApp = Bundle.main.bundlePath.hasSuffix(".app")
let target = isApp ? Bundle.main.bundlePath : (Bundle.main.executablePath ?? Bundle.main.bundlePath)
let p = Process()
p.executableURL = URL(fileURLWithPath: "/bin/sh")
p.arguments = ["-c", isApp ? "sleep 0.5; /usr/bin/open \"$1\"" : "sleep 0.5; \"$1\" >/dev/null 2>&1 &", "sh", target]
try? p.run()
NSApp.terminate(nil)
}
@MainActor
func openScreenRecordingSettings() {
NSWorkspace.shared.open(URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture")!)
}
@MainActor
func openAccessibilitySettings() {
NSWorkspace.shared.open(URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility")!)
}
@MainActor
func showPermissionAlert() {
NSApp.activate(ignoringOtherApps: true)
let a = NSAlert()
a.alertStyle = .warning
a.messageText = "Screen Recording Permission Needed"
a.informativeText = "Give Screen Recording permission in System Settings, then restart Unlag Neo."
a.addButton(withTitle: "Open Settings")
a.addButton(withTitle: "Restart Unlag Neo")
a.addButton(withTitle: "Quit Unlag Neo")
switch a.runModal() {
case .alertFirstButtonReturn:
openScreenRecordingSettings()
let b = NSAlert()
b.alertStyle = .informational
b.messageText = "Restart Unlag Neo After Permission"
b.informativeText = "After enabling Screen Recording permission, restart Unlag Neo."
b.addButton(withTitle: "Restart Unlag Neo")
b.addButton(withTitle: "Quit Unlag Neo")
if b.runModal() == .alertFirstButtonReturn {
relaunchUnlag()
} else {
NSApp.terminate(nil)
}
case .alertSecondButtonReturn:
relaunchUnlag()
default:
NSApp.terminate(nil)
}
}
@MainActor
func showAccessibilityAlert() {
guard !showedAccessibilityAlert else { return }
showedAccessibilityAlert = true
NSApp.activate(ignoringOtherApps: true)
let a = NSAlert()
a.alertStyle = .warning
a.messageText = "Accessibility Permission Needed"
a.informativeText = "Give Accessibility permission in System Settings, then restart Unlag Neo."
a.addButton(withTitle: "Open Settings")
a.addButton(withTitle: "Restart Unlag Neo")
a.addButton(withTitle: "Quit Unlag Neo")
switch a.runModal() {
case .alertFirstButtonReturn:
openAccessibilitySettings()
let b = NSAlert()
b.alertStyle = .informational
b.messageText = "Restart Unlag Neo After Permission"
b.informativeText = "After enabling Accessibility permission, restart Unlag Neo."
b.addButton(withTitle: "Restart Unlag Neo")
b.addButton(withTitle: "Quit Unlag Neo")
if b.runModal() == .alertFirstButtonReturn {
relaunchUnlag()
} else {
NSApp.terminate(nil)
}
case .alertSecondButtonReturn:
relaunchUnlag()
default:
NSApp.terminate(nil)
}
}
@MainActor
func requireScreenRecordingPermission() throws {
guard !CGPreflightScreenCaptureAccess() else { return }
_ = CGRequestScreenCaptureAccess()
if !showedPermissionAlert {
showedPermissionAlert = true
showPermissionAlert()
}
throw CaptureError("Missing Screen Recording permission.")
}
func axCopy(_ element: AXUIElement, _ attr: String) -> AnyObject? {
var value: CFTypeRef?
return AXUIElementCopyAttributeValue(element, attr as CFString, &value) == .success ? value : nil
}
func axBool(_ element: AXUIElement, _ attr: String) -> Bool {
(axCopy(element, attr) as? NSNumber)?.boolValue ?? false
}
func axValue(_ element: AXUIElement, _ attr: String, _ type: AXValueType) -> AXValue? {
guard let v = axCopy(element, attr) else { return nil }
let axv = v as! AXValue
return AXValueGetType(axv) == type ? axv : nil
}
func axRect(_ element: AXUIElement) -> CGRect? {
guard let p = axValue(element, kAXPositionAttribute, .cgPoint),
let s = axValue(element, kAXSizeAttribute, .cgSize)
else { return nil }
var point = CGPoint.zero
var size = CGSize.zero
guard AXValueGetValue(p, .cgPoint, &point),
AXValueGetValue(s, .cgSize, &size)
else { return nil }
return CGRect(origin: point, size: size)
}
func focusedOrMainWindow(_ app: AXUIElement) -> AXUIElement? {
if let w = axCopy(app, kAXFocusedWindowAttribute) {
return (w as! AXUIElement)
}
if let w = axCopy(app, kAXMainWindowAttribute) {
return (w as! AXUIElement)
}
return nil
}
func axWindows(_ app: AXUIElement) -> [AXUIElement] {
guard let xs = axCopy(app, kAXWindowsAttribute) as? [AnyObject] else { return [] }
return xs.map { $0 as! AXUIElement }
}
func displayRects() -> [CGRect] {
var count: UInt32 = 0
CGGetActiveDisplayList(0, nil, &count)
var ids = [CGDirectDisplayID](repeating: 0, count: Int(count))
CGGetActiveDisplayList(count, &ids, &count)
return ids.prefix(Int(count)).map { CGDisplayBounds($0) }
}
func mainDisplayIsAsleep() -> Bool {
CGDisplayIsAsleep(CGMainDisplayID()) != 0
}
func fillsDisplay(_ rect: CGRect, _ display: CGRect) -> Bool {
let t: CGFloat = 8
let i = rect.intersection(display)
return i.width >= display.width - t && i.height >= display.height - t
}
func windowLooksFullscreen(_ window: AXUIElement, _ displays: [CGRect]) -> Bool {
if axBool(window, "AXFullScreen") {
return true
}
guard let rect = axRect(window) else { return false }
return displays.contains { fillsDisplay(rect, $0) }
}
func cgFullscreenWindowVisible() -> Bool {
guard let app = NSWorkspace.shared.frontmostApplication else { return false }
guard app.bundleIdentifier != "com.apple.finder" else { return false }
let pid = Int(app.processIdentifier)
let selfPid = Int(ProcessInfo.processInfo.processIdentifier)
let displays = displayRects()
guard pid != selfPid,
let windows = CGWindowListCopyWindowInfo([.optionOnScreenOnly, .excludeDesktopElements], kCGNullWindowID) as? [[String: Any]]
else {
return false
}
for w in windows {
guard let owner = (w[kCGWindowOwnerPID as String] as? NSNumber)?.intValue,
owner == pid,
let layer = (w[kCGWindowLayer as String] as? NSNumber)?.intValue,
layer == 0,
let alpha = (w[kCGWindowAlpha as String] as? NSNumber)?.doubleValue,
alpha > 0,
let bounds = w[kCGWindowBounds as String] as? NSDictionary,
let rect = CGRect(dictionaryRepresentation: bounds as CFDictionary)
else { continue }
if displays.contains(where: { fillsDisplay(rect, $0) }) {
return true
}
}
return false
}
func hasFullscreenWindowVisible() -> Bool {
guard NSWorkspace.shared.frontmostApplication?.bundleIdentifier != "com.apple.finder" else {
return false
}
let displays = displayRects()
if AXIsProcessTrusted(),
let app = NSWorkspace.shared.frontmostApplication,
app.processIdentifier != ProcessInfo.processInfo.processIdentifier {
let axApp = AXUIElementCreateApplication(app.processIdentifier)
var windows = axWindows(axApp)
if let w = focusedOrMainWindow(axApp) {
windows.insert(w, at: 0)
}
for w in windows {
if windowLooksFullscreen(w, displays) {
return true
}
}
}
return cgFullscreenWindowVisible()
}
func statusIcon() -> NSImage {
let size = NSSize(width: 22, height: 22)
let img = NSImage(size: size)
img.lockFocus()
NSColor.labelColor.setStroke()
let circle = NSBezierPath(ovalIn: NSRect(x: 4, y: 4, width: 14, height: 14))
circle.lineWidth = 1.5
circle.stroke()
let attrs: [NSAttributedString.Key: Any] = [
.font: NSFont.boldSystemFont(ofSize: 7),
.foregroundColor: NSColor.labelColor
]
let s = NSString(string: "UN")
let textSize = s.size(withAttributes: attrs)
s.draw(
at: NSPoint(
x: (size.width - textSize.width) / 2,
y: (size.height - textSize.height) / 2 + 0
),
withAttributes: attrs
)
img.unlockFocus()
img.isTemplate = true
return img
}
func axCallback(_ observer: AXObserver, _ element: AXUIElement, _ notification: CFString, _ refcon: UnsafeMutableRawPointer?) {
guard let refcon else { return }
let app = Unmanaged<AppDelegate>.fromOpaque(refcon).takeUnretainedValue()
DispatchQueue.main.async {
app.axEvent()
}
}
final class NullStreamOutput: NSObject, SCStreamOutput {
private(set) var frameCount = 0
private var printed = false
func stream(_ stream: SCStream, didOutputSampleBuffer sampleBuffer: CMSampleBuffer, of type: SCStreamOutputType) {
guard type == .screen, sampleBuffer.isValid,
let a = CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, createIfNecessary: false) as? [[SCStreamFrameInfo: Any]],
let raw = a.first?[SCStreamFrameInfo.status] as? Int,
SCFrameStatus(rawValue: raw) == .complete
else { return }
frameCount += 1
guard !printed else { return }
printed = true
if let b = CMSampleBufferGetImageBuffer(sampleBuffer) {
print("Receiving \(CVPixelBufferGetWidth(b))x\(CVPixelBufferGetHeight(b)) frames every 10 seconds and dropping them.")
} else {
print("Receiving frames every 10 seconds and dropping them.")
}
}
}
actor Capture {
private var stream: SCStream?
private var output: NullStreamOutput?
private var starting = false
private let queue = DispatchQueue(label: "u.capture.samples")
func start() async {
guard stream == nil, !starting else { return }
guard !mainDisplayIsAsleep() else {
print("Display is asleep; capture start skipped.")
return
}
starting = true
defer { starting = false }
do {
try await MainActor.run { try requireScreenRecordingPermission() }
let id = CGMainDisplayID()
let content = try await SCShareableContent.current
guard let display = content.displays.first(where: { $0.displayID == id }) else {
throw CaptureError("Could not find the main display.")
}
let c = SCStreamConfiguration()
c.sourceRect = CGRect(x: 0, y: 0, width: 1, height: 1)
c.width = 1
c.height = 1
c.minimumFrameInterval = CMTime(value: 10, timescale: 1)
c.queueDepth = 4
c.showsCursor = true
c.capturesAudio = false
c.pixelFormat = kCVPixelFormatType_32BGRA
let o = NullStreamOutput()
let s = SCStream(filter: SCContentFilter(display: display, excludingWindows: []), configuration: c, delegate: nil)
try s.addStreamOutput(o, type: .screen, sampleHandlerQueue: queue)
output = o
stream = s
guard CGDisplayIsAsleep(id) == 0 else {
stream = nil
output = nil
throw CaptureError("Display went to sleep before capture started.")
}
print("Starting 1x1 null screen capture with cursor enabled.")
try await s.startCapture()
print("Running. One frame every 10 seconds.")
} catch {
stream = nil
output = nil
print("Error:", error)
}
}
func stop() async {
guard let s = stream else { return }
let n = output?.frameCount ?? 0
stream = nil
output = nil
do {
try await s.stopCapture()
print("Stopped. Dropped \(n) frames.")
} catch {
print("Stop error:", error)
}
}
func restart() async {
print("Restarting capture.")
await stop()
await start()
}
}
final class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate {
private var item: NSStatusItem!
private var enabledSwitch: NSSwitch!
private var fullscreenSwitch: NSSwitch!
private var loginSwitch: NSSwitch!
private var isEnabled = true
private var pauseInFullscreen = UserDefaults.standard.bool(forKey: "pauseInFullscreen")
private var captureWanted: Bool?
private var pendingSecondApply = false
private var screenSleeping = false
private var wakeSettling = false
private var wakeGeneration = 0
private var fullscreenDebounce: DispatchWorkItem?
private var axObserver: AXObserver?
private var axApp: AXUIElement?
private var axObservedWindows: [AXUIElement] = []
private let capture = Capture()
func applicationWillFinishLaunching(_ notification: Notification) {
_ = NSApp.setActivationPolicy(.accessory)
}
func applicationDidFinishLaunching(_ notification: Notification) {
item = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
item.button?.image = statusIcon()
item.button?.imagePosition = .imageOnly
let menu = NSMenu()
menu.delegate = self
let enabledRow = switchRow(title: "Unlag Neo Enabled", state: .on, action: #selector(toggleEnabled))
enabledSwitch = enabledRow.1
menu.addItem(enabledRow.0)
let fullscreenRow = switchRow(title: "Pause in Fullscreen", state: pauseInFullscreen ? .on : .off, action: #selector(toggleFullscreenPause))
fullscreenSwitch = fullscreenRow.1
menu.addItem(fullscreenRow.0)
let loginRow = switchRow(title: "Launch at Login", state: SMAppService.mainApp.status == .enabled ? .on : .off, action: #selector(toggleLoginItem))
loginSwitch = loginRow.1
menu.addItem(loginRow.0)
menu.addItem(.separator())
let exit = NSMenuItem(title: "Exit", action: #selector(exit), keyEquivalent: "q")
exit.target = self
menu.addItem(exit)
item.menu = menu
let wc = NSWorkspace.shared.notificationCenter
wc.addObserver(self, selector: #selector(sleep), name: NSWorkspace.willSleepNotification, object: nil)
wc.addObserver(self, selector: #selector(wake), name: NSWorkspace.didWakeNotification, object: nil)
wc.addObserver(self, selector: #selector(sleep), name: NSWorkspace.screensDidSleepNotification, object: nil)
wc.addObserver(self, selector: #selector(wake), name: NSWorkspace.screensDidWakeNotification, object: nil)
wc.addObserver(self, selector: #selector(frontAppChanged), name: NSWorkspace.activeSpaceDidChangeNotification, object: nil)
wc.addObserver(self, selector: #selector(frontAppChanged), name: NSWorkspace.didActivateApplicationNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(frontAppChanged), name: NSApplication.didChangeScreenParametersNotification, object: nil)
DispatchQueue.main.async {
self.screenSleeping = mainDisplayIsAsleep()
self.setupAXObserver()
self.applyCaptureState(force: true)
self.applyAgainNextMainLoop()
}
}
private func switchRow(title: String, state: NSControl.StateValue, action: Selector) -> (NSMenuItem, NSSwitch) {
let item = NSMenuItem()
let view = NSView(frame: NSRect(x: 0, y: 0, width: 250, height: 42))
let label = NSTextField(labelWithString: title)
let sw = NSSwitch()
label.translatesAutoresizingMaskIntoConstraints = false
sw.translatesAutoresizingMaskIntoConstraints = false
label.font = NSFont.menuFont(ofSize: 0)
sw.state = state
sw.target = self
sw.action = action
view.addSubview(label)
view.addSubview(sw)
NSLayoutConstraint.activate([
label.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16),
label.centerYAnchor.constraint(equalTo: view.centerYAnchor),
sw.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16),
sw.centerYAnchor.constraint(equalTo: view.centerYAnchor),
label.trailingAnchor.constraint(lessThanOrEqualTo: sw.leadingAnchor, constant: -12)
])
item.view = view
return (item, sw)
}
private func cleanupAXObserver() {
if let o = axObserver {
CFRunLoopRemoveSource(CFRunLoopGetMain(), AXObserverGetRunLoopSource(o), .defaultMode)
}
axObserver = nil
axApp = nil
axObservedWindows = []
}
private func addAX(_ observer: AXObserver, _ element: AXUIElement, _ name: String) {
_ = AXObserverAddNotification(observer, element, name as CFString, Unmanaged.passUnretained(self).toOpaque())
}
private func attachAXWindows() {
guard let observer = axObserver, let app = axApp else { return }
var windows = axWindows(app)
if let w = focusedOrMainWindow(app) {
windows.insert(w, at: 0)
}
axObservedWindows = windows
for w in windows {
addAX(observer, w, kAXMovedNotification)
addAX(observer, w, kAXResizedNotification)
addAX(observer, w, kAXUIElementDestroyedNotification)
addAX(observer, w, "AXFullScreenChanged")
}
}
private func setupAXObserver() {
cleanupAXObserver()
guard pauseInFullscreen else { return }
let opts = [kAXTrustedCheckOptionPrompt.takeUnretainedValue() as String: true] as CFDictionary
guard AXIsProcessTrustedWithOptions(opts) else {
Task { await MainActor.run { showAccessibilityAlert() } }
return
}
guard let app = NSWorkspace.shared.frontmostApplication,
app.processIdentifier != ProcessInfo.processInfo.processIdentifier,
app.bundleIdentifier != "com.apple.finder"
else { return }
var observer: AXObserver?
guard AXObserverCreate(app.processIdentifier, axCallback, &observer) == .success, let observer else { return }
axObserver = observer
axApp = AXUIElementCreateApplication(app.processIdentifier)
if let axApp {
addAX(observer, axApp, kAXFocusedWindowChangedNotification)
addAX(observer, axApp, kAXMainWindowChangedNotification)
addAX(observer, axApp, kAXWindowCreatedNotification)
}
attachAXWindows()
CFRunLoopAddSource(CFRunLoopGetMain(), AXObserverGetRunLoopSource(observer), .defaultMode)
}
private func shouldCapture() -> Bool {
isEnabled
&& !screenSleeping
&& !wakeSettling
&& !mainDisplayIsAsleep()
&& !(pauseInFullscreen && hasFullscreenWindowVisible())
}
@discardableResult
private func recoverMissedScreenWakeIfNeeded() -> Bool {
guard screenSleeping, !wakeSettling, !mainDisplayIsAsleep() else { return false }
wake()
return true
}
private func applyCaptureState(restart: Bool = false, force: Bool = false) {
let run = shouldCapture()
if !force, !restart, captureWanted == run {
return
}
captureWanted = run
Task {
if run {
if restart {
await capture.restart()
} else {
await capture.start()
}
} else {
await capture.stop()
}
}
}
private func applyAgainNextMainLoop() {
guard !pendingSecondApply else { return }
pendingSecondApply = true
DispatchQueue.main.async { [weak self] in
guard let self else { return }
self.pendingSecondApply = false
self.attachAXWindows()
self.applyCaptureState(force: true)
}
}
private func applyAfterWakeDebounce() {
wakeGeneration += 1
let generation = wakeGeneration
screenSleeping = false
wakeSettling = true
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { [weak self] in
guard let self, self.wakeGeneration == generation else { return }
if mainDisplayIsAsleep() {
self.screenSleeping = true
self.wakeSettling = false
self.applyCaptureState(force: true)
return
}
self.screenSleeping = false
self.wakeSettling = false
self.setupAXObserver()
self.applyCaptureState(restart: true, force: true)
self.applyAgainNextMainLoop()
}
}
private func applyAfterFullscreenDebounce() {
fullscreenDebounce?.cancel()
let work = DispatchWorkItem { [weak self] in
guard let self else { return }
self.fullscreenDebounce = nil
self.setupAXObserver()
self.applyCaptureState(force: true)
self.applyAgainNextMainLoop()
}
fullscreenDebounce = work
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: work)
}
func menuWillOpen(_ menu: NSMenu) {
_ = recoverMissedScreenWakeIfNeeded()
enabledSwitch.state = isEnabled ? .on : .off
fullscreenSwitch.state = pauseInFullscreen ? .on : .off
loginSwitch.state = SMAppService.mainApp.status == .enabled ? .on : .off
}
@objc private func toggleEnabled(_ sender: NSSwitch) {
isEnabled = sender.state == .on
applyCaptureState()
}
@objc private func toggleFullscreenPause(_ sender: NSSwitch) {
pauseInFullscreen = sender.state == .on
UserDefaults.standard.set(pauseInFullscreen, forKey: "pauseInFullscreen")
setupAXObserver()
applyCaptureState()
applyAgainNextMainLoop()
}
@objc private func toggleLoginItem(_ sender: NSSwitch) {
do {
if sender.state == .on {
try SMAppService.mainApp.register()
} else {
try SMAppService.mainApp.unregister()
}
sender.state = SMAppService.mainApp.status == .enabled ? .on : .off
} catch {
print("Login item error:", error)
sender.state = SMAppService.mainApp.status == .enabled ? .on : .off
}
}
@objc private func frontAppChanged() {
if recoverMissedScreenWakeIfNeeded() { return }
setupAXObserver()
applyCaptureState()
applyAgainNextMainLoop()
applyAfterFullscreenDebounce()
}
func axEvent() {
attachAXWindows()
applyCaptureState()
applyAgainNextMainLoop()
applyAfterFullscreenDebounce()
}
@objc private func sleep() {
wakeGeneration += 1
screenSleeping = true
wakeSettling = false
fullscreenDebounce?.cancel()
cleanupAXObserver()
applyCaptureState(force: true)
}
@objc private func wake() {
applyAfterWakeDebounce()
}
@objc private func exit() {
Task {
await capture.stop()
await MainActor.run { NSApp.terminate(nil) }
}
}
}
let app = NSApplication.shared
let delegate = AppDelegate()
app.delegate = delegate
_ = app.setActivationPolicy(.accessory)
app.run()
SWIFT
APP_COMPILE_PID=$!
###### ADD CHEESE ######
BASE="${APP}.app/Contents/Resources/cheese.png"
ICONSET="${APP}.app/Contents/Resources/AppIcon.iconset"
mkdir -p "$ICONSET"
swift - "$BASE" <<'SWIFT' &
import AppKit
let out = CommandLine.arguments[1]
let img = NSImage(size: NSSize(width: 1024, height: 1024))
img.lockFocus()
NSColor.clear.setFill()
NSRect(x: 0, y: 0, width: 1024, height: 1024).fill()
let cheese = "🧀" as NSString
let attrs: [NSAttributedString.Key: Any] = [
.font: NSFont(name: "Apple Color Emoji", size: 820) ?? NSFont.systemFont(ofSize: 820)
]
let size = cheese.size(withAttributes: attrs)
cheese.draw(
at: NSPoint(x: (1024 - size.width) / 2, y: (1024 - size.height) / 2 - 40),
withAttributes: attrs
)
img.unlockFocus()
let rep = NSBitmapImageRep(data: img.tiffRepresentation!)!
try! rep.representation(using: .png, properties: [:])!
.write(to: URL(fileURLWithPath: out))
SWIFT
ICON_RENDER_PID=$!
ICON_RENDER_STATUS=0
wait "$ICON_RENDER_PID" || ICON_RENDER_STATUS=1
if [ "$ICON_RENDER_STATUS" -ne 0 ]; then
final_msg="Icon render failed."
wait "$APP_COMPILE_PID" || true
exit 1
fi
SIPS_PIDS=()
for s in 16 32 128 256 512; do
sips -z "$s" "$s" "$BASE" --out "$ICONSET/icon_${s}x${s}.png" >/dev/null &
SIPS_PIDS+=("$!")
sips -z "$((s * 2))" "$((s * 2))" "$BASE" --out "$ICONSET/icon_${s}x${s}@2x.png" >/dev/null &
SIPS_PIDS+=("$!")
done
SIPS_STATUS=0
for pid in "${SIPS_PIDS[@]}"; do
wait "$pid" || SIPS_STATUS=1
done
if [ "$SIPS_STATUS" -ne 0 ]; then
final_msg="Icon resize failed."
wait "$APP_COMPILE_PID" || true
exit 1
fi
iconutil -c icns "$ICONSET" -o "${APP}.app/Contents/Resources/AppIcon.icns"
rm -rf "$BASE" "$ICONSET"
########################
BUILD_STATUS=0
wait "$APP_COMPILE_PID" || BUILD_STATUS=1
if [ "$BUILD_STATUS" -ne 0 ]; then
final_msg="Build failed."
exit 1
fi
chmod +x "${APP}.app/Contents/MacOS/${APP}"
PlistBuddy "${APP}.app/Contents/Info.plist" -c "add LSUIElement bool true"
PlistBuddy "${APP}.app/Contents/Info.plist" -c "add CFBundleIconFile string AppIcon"
PlistBuddy "${APP}.app/Contents/Info.plist" -c "add CFBundleDisplayName string ${DISPLAY_NAME}"
PlistBuddy "${APP}.app/Contents/version.plist" -c "add ProjectName string ${DISPLAY_NAME}"
find "${APP}.app"
cd -
if [ -e "${APP}.app" ]; then
echo "Error: ${APP}.app exists already"
exit 1
fi
mv "$tmp_dir/${APP}.app" "${APP}.app"
open -R "${APP}.app"
final_msg="App created."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment