fix: use a nonactivating compact panel across fullscreen spaces
This commit is contained in:
+48
-15
@@ -275,6 +275,10 @@ private enum WindowDragCheck {
|
||||
}
|
||||
|
||||
static func interceptDrags(in window: NSWindow) {
|
||||
for (method, original) in implementations.reversed() {
|
||||
imp_removeBlock(method_setImplementation(method, original))
|
||||
}
|
||||
implementations.removeAll()
|
||||
target = window
|
||||
let method = class_getInstanceMethod(type(of: window), #selector(NSWindow.performDrag(with:)))!
|
||||
let original = unsafeBitCast(method_getImplementation(method),
|
||||
@@ -309,8 +313,9 @@ private enum WindowDragCheck {
|
||||
guard ContinuousClock.now < deadline else {
|
||||
finish("Window drag timed out at \(names[probe]), phase \(phase)"); return
|
||||
}
|
||||
guard let window = NSApp.windows.first(where: { $0.canBecomeMain && !($0 is NSPanel) }) else { return }
|
||||
let model = PlayerModel.shared
|
||||
guard let window = model.compactWindow.presentationWindow
|
||||
?? NSApp.windows.first(where: { $0.canBecomeMain && !($0 is NSPanel) }) else { return }
|
||||
let fixtures = URL(fileURLWithPath: CommandLine.arguments.last!, isDirectory: true)
|
||||
if target == nil {
|
||||
guard model.canTogglePlayback else { return }
|
||||
@@ -322,10 +327,11 @@ private enum WindowDragCheck {
|
||||
window.makeMain()
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
}
|
||||
if target !== window { interceptDrags(in: window) }
|
||||
if phase == 0 {
|
||||
guard NSApp.isActive, window.isKeyWindow, window.isMainWindow else {
|
||||
guard NSApp.isActive, window.isKeyWindow, !window.canBecomeMain || window.isMainWindow else {
|
||||
window.makeKeyAndOrderFront(nil)
|
||||
window.makeMain()
|
||||
if window.canBecomeMain { window.makeMain() }
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
return
|
||||
}
|
||||
@@ -588,7 +594,10 @@ private enum WindowLayoutCheck {
|
||||
static var timer: Timer?
|
||||
static var resultURL: URL!
|
||||
static var step = 0
|
||||
static var windowIdentity: ObjectIdentifier?
|
||||
static var normalWindow: NSWindow?
|
||||
static var renderer: NSView?
|
||||
static var content: NSView?
|
||||
static var activationPolicy: NSApplication.ActivationPolicy?
|
||||
static var normalFrame = NSRect.zero
|
||||
static var compactFrame = NSRect.zero
|
||||
static var animationStart: NSRect?
|
||||
@@ -676,7 +685,28 @@ private enum WindowLayoutCheck {
|
||||
let model = PlayerModel.shared
|
||||
guard ContinuousClock.now < deadline else { finish("Window layout did not settle at step \(step)"); return }
|
||||
guard model.canTogglePlayback, model.state.has_video,
|
||||
let window = NSApp.windows.first(where: { $0.canBecomeMain && !($0 is NSPanel) }) else { return }
|
||||
let normal = normalWindow ?? NSApp.windows.first(where: { $0.canBecomeMain && !($0 is NSPanel) }),
|
||||
let video = model.videoView else { return }
|
||||
let window = model.compactWindow.presentationWindow ?? normal
|
||||
let hostedContent = model.hostedPlayerView ?? window.contentView
|
||||
if normalWindow == nil {
|
||||
normalWindow = normal
|
||||
renderer = video
|
||||
content = hostedContent
|
||||
activationPolicy = NSApp.activationPolicy()
|
||||
}
|
||||
guard video === renderer, hostedContent === content, hostedContent?.window === window,
|
||||
video.window === window, NSApp.activationPolicy() == activationPolicy else {
|
||||
finish("Transition replaced the content/renderer or changed app activation policy"); return
|
||||
}
|
||||
if window !== normal {
|
||||
guard let panel = window as? NSPanel, panel.styleMask.contains(.nonactivatingPanel),
|
||||
!normal.isVisible, !normal.isKeyWindow else {
|
||||
finish("Compact presentation is not an independent nonactivating panel hiding the normal window"); return
|
||||
}
|
||||
} else if model.isCompact {
|
||||
finish("Compact playback retained the normal window"); return
|
||||
}
|
||||
if model.compactWindow.isTransitioning || model.isQueueTransitioning {
|
||||
window.layoutIfNeeded()
|
||||
if model.isQueueTransitioning && !model.isCompact && !sidebarHeaderIsVisible(in: window) {
|
||||
@@ -718,8 +748,9 @@ private enum WindowLayoutCheck {
|
||||
queueVideoSize = nil
|
||||
observingQueue = false
|
||||
}
|
||||
if windowIdentity == nil { windowIdentity = ObjectIdentifier(window) }
|
||||
guard ObjectIdentifier(window) == windowIdentity else { finish("Transition replaced the playback window"); return }
|
||||
guard model.isCompact ? window !== normal : window === normal else {
|
||||
finish("Settled presentation does not match the requested mode"); return
|
||||
}
|
||||
if model.fullScreenIntent != nil { return }
|
||||
window.layoutIfNeeded()
|
||||
if compactQueueOnly && step == 0 {
|
||||
@@ -1079,20 +1110,21 @@ private enum CLIInstanceCheck {
|
||||
let model = PlayerModel.shared
|
||||
let pid = ProcessInfo.processInfo.processIdentifier
|
||||
// Text input can create non-main auxiliary windows; count actual player windows.
|
||||
let windows = NSApp.windows.filter { $0.canBecomeMain && !($0 is NSPanel) && $0.contentView != nil }
|
||||
let windows = NSApp.windows.filter { $0.canBecomeMain && !($0 is NSPanel) }
|
||||
let presentation = model.compactWindow.presentationWindow ?? windows.first
|
||||
for window in windows { window.alphaValue = 0 }
|
||||
let request = directory.appendingPathComponent("keys-\(pid).json")
|
||||
var pendingRequestID = ""
|
||||
if let data = try? Data(contentsOf: request),
|
||||
let command = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
|
||||
let id = command["id"] as? String, id != requestID, let window = windows.first {
|
||||
let id = command["id"] as? String, id != requestID, let window = presentation {
|
||||
pendingRequestID = id
|
||||
if !NSApp.isActive || !window.isKeyWindow || !window.isMainWindow {
|
||||
if !NSApp.isActive || !window.isKeyWindow || window.canBecomeMain && !window.isMainWindow {
|
||||
// Synthetic native key requests need foreground focus; cooperative activation
|
||||
// can be refused by the unrelated app currently active on the workstation.
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
window.makeKeyAndOrderFront(nil)
|
||||
window.makeMain()
|
||||
if window.canBecomeMain { window.makeMain() }
|
||||
} else {
|
||||
requestID = id
|
||||
if let media = command["media"] as? String { model.open(URL(fileURLWithPath: media)) }
|
||||
@@ -1140,8 +1172,9 @@ private enum CLIInstanceCheck {
|
||||
}
|
||||
}
|
||||
if let events = command["events"] as? [[String: Any]] {
|
||||
window.makeKey()
|
||||
for input in events {
|
||||
let window = model.compactWindow.presentationWindow ?? window
|
||||
window.makeKey()
|
||||
if let title = input["select"] as? String {
|
||||
guard let menu = NSApp.mainMenu else { fatalError("Native application menu is missing") }
|
||||
prepareMenu(menu)
|
||||
@@ -1206,14 +1239,14 @@ private enum CLIInstanceCheck {
|
||||
"requestID": requestID,
|
||||
"pendingRequestID": pendingRequestID,
|
||||
"active": NSApp.isActive,
|
||||
"keyWindow": windows.first?.isKeyWindow ?? false,
|
||||
"mainWindow": windows.first?.isMainWindow ?? false,
|
||||
"keyWindow": presentation?.isKeyWindow ?? false,
|
||||
"mainWindow": presentation?.isMainWindow ?? false,
|
||||
"editorText": editorText,
|
||||
"compact": model.isCompact,
|
||||
"showsQueue": model.showsQueue,
|
||||
"queueURLs": model.queue.items.map(\.url.absoluteString),
|
||||
"queueIndex": model.queue.items.firstIndex { $0.id == model.queue.currentID } ?? -1,
|
||||
"window": windowSnapshot(windows.first),
|
||||
"window": windowSnapshot(presentation),
|
||||
"onTop": model.isOnTop,
|
||||
]
|
||||
do {
|
||||
|
||||
Reference in New Issue
Block a user