@@ -194,12 +194,24 @@ export abstract class BaseWindow extends Disposable implements IBaseWindow {
194
194
if ( this . environmentMainService . args [ 'open-devtools' ] === true ) {
195
195
win . webContents . openDevTools ( ) ;
196
196
}
197
+
198
+ // macOS: Window Fullscreen Transitions
199
+ if ( isMacintosh ) {
200
+ this . _register ( this . onDidEnterFullScreen ( ( ) => {
201
+ this . joinNativeFullScreenTransition ?. complete ( true ) ;
202
+ } ) ) ;
203
+
204
+ this . _register ( this . onDidLeaveFullScreen ( ( ) => {
205
+ this . joinNativeFullScreenTransition ?. complete ( true ) ;
206
+ } ) ) ;
207
+ }
197
208
}
198
209
199
210
constructor (
200
211
protected readonly configurationService : IConfigurationService ,
201
212
protected readonly stateService : IStateService ,
202
- protected readonly environmentMainService : IEnvironmentMainService
213
+ protected readonly environmentMainService : IEnvironmentMainService ,
214
+ protected readonly logService : ILogService
203
215
) {
204
216
super ( ) ;
205
217
}
@@ -333,21 +345,18 @@ export abstract class BaseWindow extends Disposable implements IBaseWindow {
333
345
334
346
//#region Fullscreen
335
347
336
- // TODO@electron workaround for https://github.com/electron/electron/issues/35360
337
- // where on macOS the window will report a wrong state for `isFullScreen()` while
338
- // transitioning into and out of native full screen.
339
- protected transientIsNativeFullScreen : boolean | undefined = undefined ;
340
- protected joinNativeFullScreenTransition : DeferredPromise < void > | undefined = undefined ;
348
+ private transientIsNativeFullScreen : boolean | undefined = undefined ;
349
+ private joinNativeFullScreenTransition : DeferredPromise < boolean > | undefined = undefined ;
341
350
342
351
toggleFullScreen ( ) : void {
343
- this . setFullScreen ( ! this . isFullScreen ) ;
352
+ this . setFullScreen ( ! this . isFullScreen , false ) ;
344
353
}
345
354
346
- protected setFullScreen ( fullscreen : boolean ) : void {
355
+ protected setFullScreen ( fullscreen : boolean , fromRestore : boolean ) : void {
347
356
348
357
// Set fullscreen state
349
358
if ( useNativeFullScreen ( this . configurationService ) ) {
350
- this . setNativeFullScreen ( fullscreen ) ;
359
+ this . setNativeFullScreen ( fullscreen , fromRestore ) ;
351
360
} else {
352
361
this . setSimpleFullScreen ( fullscreen ) ;
353
362
}
@@ -365,31 +374,56 @@ export abstract class BaseWindow extends Disposable implements IBaseWindow {
365
374
return Boolean ( isFullScreen || isSimpleFullScreen ) ;
366
375
}
367
376
368
- private setNativeFullScreen ( fullscreen : boolean ) : void {
377
+ private setNativeFullScreen ( fullscreen : boolean , fromRestore : boolean ) : void {
369
378
const win = this . win ;
370
379
if ( win ?. isSimpleFullScreen ( ) ) {
371
380
win ?. setSimpleFullScreen ( false ) ;
372
381
}
373
382
374
- this . doSetNativeFullScreen ( fullscreen ) ;
383
+ this . doSetNativeFullScreen ( fullscreen , fromRestore ) ;
375
384
}
376
385
377
- private doSetNativeFullScreen ( fullscreen : boolean ) : void {
386
+ private doSetNativeFullScreen ( fullscreen : boolean , fromRestore : boolean ) : void {
378
387
if ( isMacintosh ) {
388
+
389
+ // macOS: Electron windows report `false` for `isFullScreen()` for as long
390
+ // as the fullscreen transition animation takes place. As such, we need to
391
+ // listen to the transition events and carry around an intermediate state
392
+ // for knowing if we are in fullscreen or not
393
+ // Refs: https://github.com/electron/electron/issues/35360
394
+
379
395
this . transientIsNativeFullScreen = fullscreen ;
380
- this . joinNativeFullScreenTransition = new DeferredPromise < void > ( ) ;
381
- Promise . race ( [
382
- this . joinNativeFullScreenTransition . p ,
383
- // still timeout after some time in case the transition is unusually slow
384
- // this can easily happen for an OS update where macOS tries to reopen
385
- // previous applications and that can take multiple seconds, probably due
386
- // to security checks. its worth noting that if this takes more than
387
- // 10 seconds, users would see a window that is not-fullscreen but without
388
- // custom titlebar...
389
- timeout ( 10000 )
390
- ] ) . finally ( ( ) => {
396
+
397
+ const joinNativeFullScreenTransition = this . joinNativeFullScreenTransition = new DeferredPromise < boolean > ( ) ;
398
+ ( async ( ) => {
399
+ const transitioned = await Promise . race ( [
400
+ joinNativeFullScreenTransition . p ,
401
+ timeout ( 10000 ) . then ( ( ) => false )
402
+ ] ) ;
403
+
404
+ if ( this . joinNativeFullScreenTransition !== joinNativeFullScreenTransition ) {
405
+ return ; // another transition was requested later
406
+ }
407
+
391
408
this . transientIsNativeFullScreen = undefined ;
392
- } ) ;
409
+ this . joinNativeFullScreenTransition = undefined ;
410
+
411
+ if ( ! transitioned && fullscreen && fromRestore ) {
412
+
413
+ // We have seen requests for fullscreen failing eventually after some
414
+ // time, for example when an OS update was performed and windows restore.
415
+ // In those cases a user would find a window that is not in fullscreen
416
+ // but also does not show any custom titlebar (and thus window controls)
417
+ // because we think the window is in fullscreen.
418
+ //
419
+ // As a workaround in that case we emit a warning and leave fullscreen
420
+ // so that at least the window controls are back.
421
+
422
+ this . logService . warn ( 'window: native macOS fullscreen transition did not happen within 10s from restoring' ) ;
423
+
424
+ this . _onDidLeaveFullScreen . fire ( ) ;
425
+ }
426
+ } ) ( ) ;
393
427
}
394
428
395
429
const win = this . win ;
@@ -399,7 +433,7 @@ export abstract class BaseWindow extends Disposable implements IBaseWindow {
399
433
private setSimpleFullScreen ( fullscreen : boolean ) : void {
400
434
const win = this . win ;
401
435
if ( win ?. isFullScreen ( ) ) {
402
- this . doSetNativeFullScreen ( false ) ;
436
+ this . doSetNativeFullScreen ( false , false ) ;
403
437
}
404
438
405
439
win ?. setSimpleFullScreen ( fullscreen ) ;
@@ -486,7 +520,7 @@ export class CodeWindow extends BaseWindow implements ICodeWindow {
486
520
487
521
constructor (
488
522
config : IWindowCreationOptions ,
489
- @ILogService private readonly logService : ILogService ,
523
+ @ILogService logService : ILogService ,
490
524
@ILoggerMainService private readonly loggerMainService : ILoggerMainService ,
491
525
@IEnvironmentMainService environmentMainService : IEnvironmentMainService ,
492
526
@IPolicyService private readonly policyService : IPolicyService ,
@@ -507,7 +541,7 @@ export class CodeWindow extends BaseWindow implements ICodeWindow {
507
541
@IStateService stateService : IStateService ,
508
542
@IInstantiationService instantiationService : IInstantiationService
509
543
) {
510
- super ( configurationService , stateService , environmentMainService ) ;
544
+ super ( configurationService , stateService , environmentMainService , logService ) ;
511
545
512
546
//#region create browser window
513
547
{
@@ -570,7 +604,7 @@ export class CodeWindow extends BaseWindow implements ICodeWindow {
570
604
this . _win . maximize ( ) ;
571
605
572
606
if ( this . windowState . mode === WindowMode . Fullscreen ) {
573
- this . setFullScreen ( true ) ;
607
+ this . setFullScreen ( true , true ) ;
574
608
}
575
609
576
610
// to reduce flicker from the default window size
@@ -682,16 +716,10 @@ export class CodeWindow extends BaseWindow implements ICodeWindow {
682
716
// Window Fullscreen
683
717
this . _register ( this . onDidEnterFullScreen ( ( ) => {
684
718
this . sendWhenReady ( 'vscode:enterFullScreen' , CancellationToken . None ) ;
685
-
686
- this . joinNativeFullScreenTransition ?. complete ( ) ;
687
- this . joinNativeFullScreenTransition = undefined ;
688
719
} ) ) ;
689
720
690
721
this . _register ( this . onDidLeaveFullScreen ( ( ) => {
691
722
this . sendWhenReady ( 'vscode:leaveFullScreen' , CancellationToken . None ) ;
692
-
693
- this . joinNativeFullScreenTransition ?. complete ( ) ;
694
- this . joinNativeFullScreenTransition = undefined ;
695
723
} ) ) ;
696
724
697
725
// Handle configuration changes
@@ -1384,8 +1412,8 @@ export class CodeWindow extends BaseWindow implements ICodeWindow {
1384
1412
return { x, y, width, height } ;
1385
1413
}
1386
1414
1387
- protected override setFullScreen ( fullscreen : boolean ) : void {
1388
- super . setFullScreen ( fullscreen ) ;
1415
+ protected override setFullScreen ( fullscreen : boolean , fromRestore : boolean ) : void {
1416
+ super . setFullScreen ( fullscreen , fromRestore ) ;
1389
1417
1390
1418
// Events
1391
1419
this . sendWhenReady ( fullscreen ? 'vscode:enterFullScreen' : 'vscode:leaveFullScreen' , CancellationToken . None ) ;
0 commit comments