native macOS codings agent orchestrator
6
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'main' of github.com:onevcat/Prowl

onevcat 21805e80 5fe7dbc8

+55 -2
+17
supacode/Infrastructure/Ghostty/GhosttySurfaceView.swift
··· 1055 1055 func setOcclusion(_ visible: Bool) { 1056 1056 guard let surface else { 1057 1057 guard skipsSurfaceCreationForTesting else { return } 1058 + // Occluding (pausing render) is always safe, even without a view 1059 + // hierarchy. This handles restored surfaces that haven't been attached 1060 + // to a window yet. 1061 + if !visible { 1062 + guard occlusionState.prepareToApply(false) else { return } 1063 + onOcclusionAppliedForTesting?(false) 1064 + return 1065 + } 1058 1066 guard isReadyToApplyOcclusion else { 1059 1067 if occlusionState.desired != visible { 1060 1068 surfaceLogger.info( ··· 1067 1075 } 1068 1076 guard occlusionState.prepareToApply(visible) else { return } 1069 1077 onOcclusionAppliedForTesting?(visible) 1078 + return 1079 + } 1080 + // Occluding (pausing render) is always safe, even without a view 1081 + // hierarchy. This stops restored surfaces from spinning the GPU when 1082 + // they are not displayed. 1083 + if !visible { 1084 + guard occlusionState.prepareToApply(false) else { return } 1085 + onOcclusionAppliedForTesting?(false) 1086 + ghostty_surface_set_occlusion(surface, false) 1070 1087 return 1071 1088 } 1072 1089 guard isReadyToApplyOcclusion else {
+38 -2
supacodeTests/GhosttySurfaceViewTests.swift
··· 142 142 surfaceView.handleAttachmentChangeForTesting() 143 143 await drainMainQueue() 144 144 145 + // Occluding (false) applies immediately even while detached to stop 146 + // the render loop. Un-occluding (true) is deferred until reattached. 145 147 surfaceView.setOcclusion(false) 146 148 surfaceView.setOcclusion(true) 147 149 surfaceView.setOcclusion(false) 148 150 await drainMainQueue() 149 - #expect(appliedValues == [true]) 151 + #expect(appliedValues == [true, false]) 150 152 151 153 attachmentState = (hasSuperview: true, hasWindow: true) 152 154 surfaceView.handleAttachmentChangeForTesting() 153 155 await drainMainQueue() 154 - #expect(appliedValues == [true, false]) 156 + // Reattachment re-applies the desired value (false) even though it 157 + // was already applied, because invalidateForAttachmentChange clears 158 + // the applied cache. 159 + #expect(appliedValues == [true, false, false]) 160 + } 161 + 162 + @Test func occlusionFalseAppliesImmediatelyWithoutViewAttachment() async { 163 + let runtime = GhosttyRuntime() 164 + let surfaceView = GhosttySurfaceView( 165 + runtime: runtime, 166 + workingDirectory: nil, 167 + context: GHOSTTY_SURFACE_CONTEXT_TAB, 168 + skipsSurfaceCreationForTesting: true 169 + ) 170 + var appliedValues: [Bool] = [] 171 + surfaceView.onOcclusionAppliedForTesting = { appliedValues.append($0) } 172 + var attachmentState = (hasSuperview: false, hasWindow: false) 173 + surfaceView.attachmentStateForTesting = { attachmentState } 174 + 175 + // Occluding without a view hierarchy applies immediately (stops the 176 + // Metal render loop for restored surfaces that are never displayed). 177 + surfaceView.setOcclusion(false) 178 + await drainMainQueue() 179 + #expect(appliedValues == [false]) 180 + 181 + // Un-occluding without a view hierarchy is deferred. 182 + surfaceView.setOcclusion(true) 183 + await drainMainQueue() 184 + #expect(appliedValues == [false]) 185 + 186 + // Once attached, the deferred un-occlude is applied. 187 + attachmentState = (hasSuperview: true, hasWindow: true) 188 + surfaceView.handleAttachmentChangeForTesting() 189 + await drainMainQueue() 190 + #expect(appliedValues == [false, true]) 155 191 } 156 192 157 193 @Test func occlusionCanRecoverWhenAttachmentCallbackIsMissedAfterReattachment() async {