Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 36 additions & 12 deletions Sources/AppIconDockTilePlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ final class CmuxDockTilePlugin: NSObject, NSDockTilePlugIn {
}

func setDockTile(_ dockTile: NSDockTile?) {
Self.performOnMain { [self] in
setDockTileOnMain(dockTile)
}
}

private func setDockTileOnMain(_ dockTile: NSDockTile?) {
Self.assertMainQueue()

if let iconChangeObserver {
DistributedNotificationCenter.default().removeObserver(iconChangeObserver)
self.iconChangeObserver = nil
Expand All @@ -53,7 +61,7 @@ final class CmuxDockTilePlugin: NSObject, NSDockTilePlugIn {
iconChangeObserver = DistributedNotificationCenter.default().addObserver(
forName: cmuxAppIconDidChangeNotification,
object: nil,
queue: nil
queue: .main
) { [weak self] _ in
guard let self else { return }
self.updateDockTile(dockTile)
Expand Down Expand Up @@ -91,6 +99,8 @@ final class CmuxDockTilePlugin: NSObject, NSDockTilePlugIn {
}

private func updateDockTile(_ dockTile: NSDockTile) {
Self.assertMainQueue()

let mode = DockTileAppIconMode(defaultsValue: appDefaults?.string(forKey: cmuxAppIconModeKey))
let isDarkAppearance = NSApp?.effectiveAppearance.bestMatch(from: [.darkAqua, .aqua]) == .darkAqua
guard let appBundleURL else {
Expand All @@ -117,6 +127,20 @@ final class CmuxDockTilePlugin: NSObject, NSDockTilePlugIn {
dockTile.showIcon(icon)
}

private static func performOnMain(_ work: @escaping () -> Void) {
if Thread.isMainThread {
work()
} else {
DispatchQueue.main.async(execute: work)
}
}

fileprivate static func assertMainQueue() {
#if DEBUG
dispatchPrecondition(condition: .onQueue(.main))
#endif
}

/// Determine the enclosing app bundle for the dock tile plugin bundle.
static func appBundleURL(for pluginBundleURL: URL) -> URL? {
var url = pluginBundleURL
Expand All @@ -137,20 +161,20 @@ final class CmuxDockTilePlugin: NSObject, NSDockTilePlugIn {

private extension NSDockTile {
func showDefaultAppIcon() {
DispatchQueue.main.async {
self.contentView = nil
self.display()
}
CmuxDockTilePlugin.assertMainQueue()

contentView = nil
display()
}

func showIcon(_ newIcon: NSImage) {
DispatchQueue.main.async {
let iconView = NSImageView(frame: CGRect(origin: .zero, size: self.size))
iconView.wantsLayer = true
iconView.image = newIcon
self.contentView = iconView
self.display()
}
CmuxDockTilePlugin.assertMainQueue()

let iconView = NSImageView(frame: CGRect(origin: .zero, size: size))
iconView.wantsLayer = true
iconView.image = newIcon
contentView = iconView
display()
}
}

Expand Down
4 changes: 4 additions & 0 deletions Sources/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15240,6 +15240,10 @@ final class DraggableFolderNSView: NSView, NSDraggingSource {
}

func updateIcon() {
#if DEBUG
dispatchPrecondition(condition: .onQueue(.main))
#endif

let icon = NSWorkspace.shared.icon(forFile: directory)
icon.size = NSSize(width: 16, height: 16)
imageView.image = icon
Expand Down
4 changes: 2 additions & 2 deletions Sources/FileExplorerView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1486,6 +1486,7 @@ final class FileExplorerHeaderView: NSView {
}

private func applyHeaderState() {
assert(Thread.isMainThread, "AppKit image updates must run on the main thread")
let config = NSImage.SymbolConfiguration(pointSize: 11, weight: .regular)
if let quickSearchQuery {
iconView.image = NSImage(systemSymbolName: "magnifyingglass", accessibilityDescription: nil)?
Expand Down Expand Up @@ -1580,11 +1581,10 @@ final class FileExplorerCellView: NSTableCellView {
}

func configure(with node: FileExplorerNode, gitStatus: GitFileStatus? = nil) {
assert(Thread.isMainThread, "AppKit image updates must run on the main thread")
let style = FileExplorerStyle.current

nameLabel.stringValue = node.name
nameLabel.font = style.nameFont

iconWidthConstraint.constant = style.iconSize
iconHeightConstraint.constant = style.iconSize
iconToTextConstraint.constant = style.iconToTextSpacing
Expand Down
8 changes: 8 additions & 0 deletions Sources/GhosttyTerminalView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4295,6 +4295,10 @@ final class TerminalSurface: Identifiable, ObservableObject {
additionalEnvironment: [String: String] = [:],
focusPlacement: TerminalSurfaceFocusPlacement = .workspace
) {
#if DEBUG
dispatchPrecondition(condition: .onQueue(.main))
#endif

self.id = UUID()
self.tabId = tabId
self.surfaceContext = context
Expand Down Expand Up @@ -9678,6 +9682,10 @@ final class GhosttySurfaceScrollView: NSView {
}

init(surfaceView: GhosttyNSView) {
#if DEBUG
dispatchPrecondition(condition: .onQueue(.main))
#endif

self.surfaceView = surfaceView
backgroundView = NSView(frame: .zero)
scrollView = GhosttyScrollView()
Expand Down
4 changes: 4 additions & 0 deletions Sources/Panels/FilePreviewPanel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1738,6 +1738,7 @@ private final class FilePreviewPDFThumbnailItemView: NSView {
}

func configure(image: NSImage?, pageNumber: String) {
assert(Thread.isMainThread, "AppKit image updates must run on the main thread")
imageView.image = image
pageLabel.stringValue = pageNumber
}
Expand Down Expand Up @@ -3033,6 +3034,7 @@ private final class FilePreviewImageContainerView: NSView {
}

func setURL(_ url: URL) {
assert(Thread.isMainThread, "AppKit image updates must run on the main thread")
guard currentURL != url else { return }
currentURL = url
documentView.imageView.image = nil
Expand All @@ -3054,6 +3056,7 @@ private final class FilePreviewImageContainerView: NSView {
}

private func applyLoadedImage(_ image: NSImage?) {
assert(Thread.isMainThread, "AppKit image updates must run on the main thread")
documentView.imageView.image = image
imageSize = normalizedSize(image?.size ?? .zero)
isFitMode = true
Expand Down Expand Up @@ -3561,6 +3564,7 @@ private final class FilePreviewMagnifyingImageView: NSImageView {
}

override func draw(_ dirtyRect: NSRect) {
assert(Thread.isMainThread, "AppKit image updates must run on the main thread")
guard let image, rotationDegrees != 0 else {
super.draw(dirtyRect)
return
Expand Down
5 changes: 5 additions & 0 deletions Sources/TerminalController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3273,6 +3273,7 @@ class TerminalController {
)
}
}
v2AttachTopApplicationProcess(to: &windowNodes)

let processSnapshot = await Task.detached(priority: .utility) {
CmuxTopProcessSnapshot.capture(includeProcessDetails: includeProcesses)
Expand Down Expand Up @@ -3393,6 +3394,10 @@ class TerminalController {
}
}

if workspaceFilter == nil {
v2AttachTopApplicationProcess(to: &windowNodes)
}

if let workspaceFilter, !workspaceFound {
return .err(
code: "not_found",
Expand Down
28 changes: 26 additions & 2 deletions Sources/TerminalControllerTopSupport.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Foundation
import Darwin

extension TerminalController {
func v2TopTagIdentifier(workspaceId: UUID, key: String) -> String {
Expand Down Expand Up @@ -45,7 +46,8 @@ extension TerminalController {
var allPIDs: Set<Int> = []
for index in windows.indices {
var workspaces = windows[index]["workspaces"] as? [[String: Any]] ?? []
var windowPIDs: Set<Int> = []
let appProcessPIDs = Set(v2TopIntArray(windows[index]["app_process_pids"]))
var windowPIDs = appProcessPIDs
var windowTopLevelPIDs: Set<Int> = []
var windowForegroundProcessGroupIDs: Set<Int> = []
for workspaceIndex in workspaces.indices {
Expand All @@ -61,9 +63,11 @@ extension TerminalController {
windowForegroundProcessGroupIDs.formUnion(v2TopIntArray(workspaces[workspaceIndex]["foreground_pgids"]))
}
windows[index]["workspaces"] = workspaces
windows[index]["app_process_pids"] = appProcessPIDs.sorted()
windowTopLevelPIDs.formUnion(processSnapshot.topLevelPIDs(for: appProcessPIDs))
windows[index]["top_level_pids"] = windowTopLevelPIDs.sorted()
windows[index]["foreground_pgids"] = windowForegroundProcessGroupIDs.sorted()
windows[index]["resources"] = processSnapshot.summaryPayload(for: windowPIDs)
windows[index]["resources"] = processSnapshot.summaryPayload(for: windowPIDs, rootPIDs: appProcessPIDs)
allPIDs.formUnion(windowPIDs)
}
return allPIDs
Expand Down Expand Up @@ -276,4 +280,24 @@ extension TerminalController {
return nil
}

nonisolated func v2AttachTopApplicationProcess(to windows: inout [[String: Any]]) {
guard let firstIndex = windows.indices.first else { return }

let appProcessID = Int(Darwin.getpid())
let targetIndex = windows.indices.first { index in
if let isKeyWindow = windows[index]["key"] as? Bool {
return isKeyWindow
}
if let isKeyWindow = windows[index]["key"] as? NSNumber {
return isKeyWindow.boolValue
}
return false
} ?? firstIndex

windows[targetIndex]["app_process_pids"] = [appProcessID]
for index in windows.indices where index != targetIndex {
windows[index]["app_process_pids"] = []
}
}
Comment thread
lawrencecchen marked this conversation as resolved.

}
Loading
Loading