mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
Merge pull request #1165 from mitchellh/dangling-notification
macos: notifications use surface UUID for lookups
This commit is contained in:
@ -332,6 +332,16 @@ class AppDelegate: NSObject,
|
|||||||
|
|
||||||
//MARK: - GhosttyAppStateDelegate
|
//MARK: - GhosttyAppStateDelegate
|
||||||
|
|
||||||
|
func findSurface(forUUID uuid: UUID) -> Ghostty.SurfaceView? {
|
||||||
|
for c in terminalManager.windows {
|
||||||
|
if let v = c.controller.surfaceTree?.findUUID(uuid: uuid) {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func configDidReload(_ state: Ghostty.AppState) {
|
func configDidReload(_ state: Ghostty.AppState) {
|
||||||
// Depending on the "window-save-state" setting we have to set the NSQuitAlwaysKeepsWindows
|
// Depending on the "window-save-state" setting we have to set the NSQuitAlwaysKeepsWindows
|
||||||
// configuration. This is the only way to carefully control whether macOS invokes the
|
// configuration. This is the only way to carefully control whether macOS invokes the
|
||||||
|
@ -5,6 +5,10 @@ import GhosttyKit
|
|||||||
protocol GhosttyAppStateDelegate: AnyObject {
|
protocol GhosttyAppStateDelegate: AnyObject {
|
||||||
/// Called when the configuration did finish reloading.
|
/// Called when the configuration did finish reloading.
|
||||||
func configDidReload(_ state: Ghostty.AppState)
|
func configDidReload(_ state: Ghostty.AppState)
|
||||||
|
|
||||||
|
/// Called when a callback needs access to a specific surface. This should return nil
|
||||||
|
/// when the surface is no longer valid.
|
||||||
|
func findSurface(forUUID uuid: UUID) -> Ghostty.SurfaceView?
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Ghostty {
|
extension Ghostty {
|
||||||
@ -608,9 +612,9 @@ extension Ghostty {
|
|||||||
/// Handle a received user notification. This is called when a user notification is clicked or dismissed by the user
|
/// Handle a received user notification. This is called when a user notification is clicked or dismissed by the user
|
||||||
func handleUserNotification(response: UNNotificationResponse) {
|
func handleUserNotification(response: UNNotificationResponse) {
|
||||||
let userInfo = response.notification.request.content.userInfo
|
let userInfo = response.notification.request.content.userInfo
|
||||||
guard let address = userInfo["address"] as? Int else { return }
|
guard let uuidString = userInfo["surface"] as? String,
|
||||||
guard let userdata = UnsafeMutableRawPointer(bitPattern: address) else { return }
|
let uuid = UUID(uuidString: uuidString),
|
||||||
let surface = Ghostty.AppState.surfaceUserdata(from: userdata)
|
let surface = delegate?.findSurface(forUUID: uuid) else { return }
|
||||||
|
|
||||||
switch (response.actionIdentifier) {
|
switch (response.actionIdentifier) {
|
||||||
case UNNotificationDefaultActionIdentifier, Ghostty.userNotificationActionShow:
|
case UNNotificationDefaultActionIdentifier, Ghostty.userNotificationActionShow:
|
||||||
@ -627,11 +631,10 @@ extension Ghostty {
|
|||||||
/// Determine if a given notification should be presented to the user when Ghostty is running in the foreground.
|
/// Determine if a given notification should be presented to the user when Ghostty is running in the foreground.
|
||||||
func shouldPresentNotification(notification: UNNotification) -> Bool {
|
func shouldPresentNotification(notification: UNNotification) -> Bool {
|
||||||
let userInfo = notification.request.content.userInfo
|
let userInfo = notification.request.content.userInfo
|
||||||
guard let address = userInfo["address"] as? Int else { return false }
|
guard let uuidString = userInfo["surface"] as? String,
|
||||||
guard let userdata = UnsafeMutableRawPointer(bitPattern: address) else { return false }
|
let uuid = UUID(uuidString: uuidString),
|
||||||
let surface = Ghostty.AppState.surfaceUserdata(from: userdata)
|
let surface = delegate?.findSurface(forUUID: uuid),
|
||||||
|
let window = surface.window else { return false }
|
||||||
guard let window = surface.window else { return false }
|
|
||||||
return !window.isKeyWindow || !surface.focused
|
return !window.isKeyWindow || !surface.focused
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1145,16 +1145,7 @@ extension Ghostty {
|
|||||||
content.body = body
|
content.body = body
|
||||||
content.sound = UNNotificationSound.default
|
content.sound = UNNotificationSound.default
|
||||||
content.categoryIdentifier = Ghostty.userNotificationCategory
|
content.categoryIdentifier = Ghostty.userNotificationCategory
|
||||||
|
content.userInfo = ["surface": self.uuid.uuidString]
|
||||||
// The userInfo must conform to NSSecureCoding, which SurfaceView
|
|
||||||
// does not. So instead we pass an integer representation of the
|
|
||||||
// SurfaceView's address, which is reconstructed back into a
|
|
||||||
// SurfaceView if the notification is clicked. This is safe to do
|
|
||||||
// so long as the SurfaceView removes all of its notifications when
|
|
||||||
// it closes so that there are no dangling pointers.
|
|
||||||
content.userInfo = [
|
|
||||||
"address": Int(bitPattern: Unmanaged.passUnretained(self).toOpaque()),
|
|
||||||
]
|
|
||||||
|
|
||||||
let uuid = UUID().uuidString
|
let uuid = UUID().uuidString
|
||||||
let request = UNNotificationRequest(
|
let request = UNNotificationRequest(
|
||||||
|
Reference in New Issue
Block a user