From 2b136dbac1cfa8e83c87267d39e60fa340073772 Mon Sep 17 00:00:00 2001 From: Yee Cheng Chin Date: Sat, 28 Dec 2024 20:12:52 -0800 Subject: [PATCH 1/3] Add tests for full screen code Basic tests to validate different full screen functionalities. Help prevents regressions such as #1515 where full screen simply stops working. Not an exhaustive test for now. Some functionalities (e.g. multi-monitor, MacBook notch) are hard to mock up and validate in tests. Some other functionalities like restoring window size, delayed full screen on startup (when setting it from gvimrc) will be added later. --- src/MacVim/MacVimTests/MacVimTests.m | 137 ++++++++++++++++++++++++++- 1 file changed, 133 insertions(+), 4 deletions(-) diff --git a/src/MacVim/MacVimTests/MacVimTests.m b/src/MacVim/MacVimTests/MacVimTests.m index b87978726f..e40d5c4fa3 100644 --- a/src/MacVim/MacVimTests/MacVimTests.m +++ b/src/MacVim/MacVimTests/MacVimTests.m @@ -15,6 +15,8 @@ #import "Miscellaneous.h" #import "MMAppController.h" #import "MMApplication.h" +#import "MMFullScreenWindow.h" +#import "MMWindow.h" #import "MMTextView.h" #import "MMWindowController.h" #import "MMVimController.h" @@ -30,10 +32,6 @@ - (void)handleMessage:(int)msgid data:(NSData *)data; @end // Test harness -@interface MMAppController (Tests) -- (NSMutableArray*)vimControllers; -@end - @implementation MMAppController (Tests) - (NSMutableArray*)vimControllers { return vimControllers; @@ -50,6 +48,12 @@ - (BOOL)inLiveResize { } @end +@implementation MMWindowController (Tests) +- (BOOL)fullScreenEnabled { + return fullScreenEnabled; +} +@end + @interface MacVimTests : XCTestCase @end @@ -733,4 +737,129 @@ - (void) testWindowResize { [self waitForVimClose]; } +- (void)waitForNativeFullscreenEnter { + XCTestExpectation *expectation = [self expectationWithDescription:@"NativeFullscreenEnter"]; + + SEL sel = @selector(windowDidEnterFullScreen:); + Method method = class_getInstanceMethod([MMWindowController class], sel); + + IMP origIMP = method_getImplementation(method); + IMP newIMP = imp_implementationWithBlock(^(id self, id notification) { + typedef void (*fn)(id,SEL,NSNotification*); + ((fn)origIMP)(self, sel, notification); + [expectation fulfill]; + }); + + method_setImplementation(method, newIMP); + [self waitForExpectations:@[expectation] timeout:10]; + method_setImplementation(method, origIMP); +} + +- (void)waitForNativeFullscreenExit { + XCTestExpectation *expectation = [self expectationWithDescription:@"NativeFullscreenExit"]; + + SEL sel = @selector(windowDidExitFullScreen:); + Method method = class_getInstanceMethod([MMWindowController class], sel); + + IMP origIMP = method_getImplementation(method); + IMP newIMP = imp_implementationWithBlock(^(id self, id notification) { + typedef void (*fn)(id,SEL,NSNotification*); + ((fn)origIMP)(self, sel, notification); + [expectation fulfill]; + }); + + method_setImplementation(method, newIMP); + [self waitForExpectations:@[expectation] timeout:10]; + method_setImplementation(method, origIMP); +} + +/// Utility to test full screen functionality in both non-native/native full +/// screen. +- (void) fullScreenTestWithNative:(BOOL)native { + MMAppController *app = MMAppController.sharedInstance; + + // Cache test defaults + NSUserDefaults *ud = NSUserDefaults.standardUserDefaults; + NSDictionary *defaults = [ud volatileDomainForName:NSArgumentDomain]; + NSMutableDictionary *newDefaults = [defaults mutableCopy]; + + // Change native full screen setting + newDefaults[MMNativeFullScreenKey] = [NSNumber numberWithBool:native]; + [ud setVolatileDomain:newDefaults forName:NSArgumentDomain]; + + [app openNewWindow:NewWindowClean activate:YES]; + [self waitForVimOpenAndMessages]; + + MMWindowController *winController = app.keyVimController.windowController; + + // Enter full screen and check that the states are properly changed. + [self sendStringToVim:@":set fu\n" withMods:0]; + if (native) { + [self waitForNativeFullscreenEnter]; + } else { + [self waitForEventHandlingAndVimProcess]; + [self waitForEventHandlingAndVimProcess]; // wait one more cycle to make sure we finished the transition + } + + XCTAssertTrue([winController fullScreenEnabled]); + if (native) { + XCTAssertTrue([winController.window isKindOfClass:[MMWindow class]]); + } else { + XCTAssertTrue([winController.window isKindOfClass:[MMFullScreenWindow class]]); + } + + // Exit full screen + [self sendStringToVim:@":set nofu\n" withMods:0]; + if (native) { + [self waitForNativeFullscreenExit]; + } else { + [self waitForEventHandlingAndVimProcess]; + [self waitForEventHandlingAndVimProcess]; // wait one more cycle to make sure we finished the transition + } + + XCTAssertFalse([winController fullScreenEnabled]); + XCTAssertTrue([winController.window isKindOfClass:[MMWindow class]]); + + // Enter full screen again + [self sendStringToVim:@":set fu\n" withMods:0]; + if (native) { + [self waitForNativeFullscreenEnter]; + } else { + [self waitForEventHandlingAndVimProcess]; + [self waitForEventHandlingAndVimProcess]; // wait one more cycle to make sure we finished the transition + } + + XCTAssertTrue([winController fullScreenEnabled]); + + // Test that resizing the vim view does not work when in full screen as we fix the window size instead + MMTextView *textView = [[[[app keyVimController] windowController] vimView] textView]; + const int fuRows = textView.maxRows; + const int fuCols = textView.maxColumns; + XCTAssertNotEqual(10, fuRows); // just some basic assumptions as full screen should have more rows/cols than this + XCTAssertNotEqual(30, fuCols); + [self sendStringToVim:@":set lines=10\n" withMods:0]; + [self sendStringToVim:@":set columns=30\n" withMods:0]; + [self waitForEventHandlingAndVimProcess]; + [self waitForEventHandlingAndVimProcess]; // need to wait twice to allow full screen to force it back + XCTAssertEqual(fuRows, textView.maxRows); + XCTAssertEqual(fuCols, textView.maxColumns); + + // Clean up + [[app keyVimController] sendMessage:VimShouldCloseMsgID data:nil]; + [self waitForVimClose]; + + XCTAssertEqual(0, [app vimControllers].count); + + // Restore settings to test defaults + [ud setVolatileDomain:defaults forName:NSArgumentDomain]; +} + +- (void) testFullScreenNonNative { + [self fullScreenTestWithNative:NO]; +} + +- (void) testFullScreenNative { + [self fullScreenTestWithNative:YES]; +} + @end From 879a21923dee346d34d4f8c798632d64eda467f9 Mon Sep 17 00:00:00 2001 From: Yee Cheng Chin Date: Mon, 6 Jan 2025 19:19:18 -0800 Subject: [PATCH 2/3] Fix non-native full screen bad interaction with window delegate This issue was detected when adding new tests for non-native full screen. When setting 'fuoptions' to empty and going full screen, the code would fail but also crash, due to a number of issues: 1. `enterFullScreen`'s setting of presentationOptions somehow triggers a window resize on the normal window, which leads to `windowDidResize` delegate being called. This is a degenerate situation as the window controller thinks we are already in full screen even though we haven't finished setting up yet, and in particular `nonFuVimViewSize` is still 0. This leads to the desired desired frame size being set incorrectly to 0. 2. `constrainRows:columns:toSize` does not sanity check the results when we have such degenerate small sizes, and end up calculating a desired rows to -1, which makes no sense. This leads to various wrong calculations. 3. MMCoreTextView loops through the grid using a size_t even though grid.rows is an int. This is a poor code practice in general and results in a crash as the loop comparison cast the -1 to size_t and the loop didn't correctly terminate. Fix 1 (the root cause) by making sure we detact the window delegate immediately when entering full screen to prevent any stray window messages from causing issues. Also safeguard `windowDidResize` so it only handles the full screen path if we have `fullScreenEnabled` set. For 2 and 3, add the sanity checks and also fix the size_t to use int when looping. For some reason this issue only showed up in CI but not in local testing, probably due to different screen environments. --- src/MacVim/MMCoreTextView.m | 8 +++++++- src/MacVim/MMFullScreenWindow.m | 22 +++++++++++++--------- src/MacVim/MMTextStorage.m | 6 ++++++ src/MacVim/MMWindowController.m | 7 ++++--- 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/MacVim/MMCoreTextView.m b/src/MacVim/MMCoreTextView.m index c687cb8480..81b21d2750 100644 --- a/src/MacVim/MMCoreTextView.m +++ b/src/MacVim/MMCoreTextView.m @@ -831,7 +831,7 @@ - (void)drawRect:(NSRect)rect // Function to draw all rows void (^drawAllRows)(void (^)(CGContextRef,CGRect,int)) = ^(void (^drawFunc)(CGContextRef,CGRect,int)){ - for (size_t r = 0; r < grid.rows; r++) { + for (int r = 0; r < grid.rows; r++) { const CGRect rowRect = [self rectForRow:(int)r column:0 numRows:1 @@ -1276,6 +1276,9 @@ - (NSSize)constrainRows:(int *)rows columns:(int *)cols toSize:(NSSize)size if (fh < 1.0f) fh = 1.0f; desiredRows = floor((size.height - ih)/fh); + // Sanity checking in case unusual window sizes lead to degenerate results + if (desiredRows < 1) + desiredRows = 1; desiredSize.height = fh*desiredRows + ih; } @@ -1285,6 +1288,9 @@ - (NSSize)constrainRows:(int *)rows columns:(int *)cols toSize:(NSSize)size if (fw < 1.0f) fw = 1.0f; desiredCols = floor((size.width - iw)/fw); + // Sanity checking in case unusual window sizes lead to degenerate results + if (desiredCols < 1) + desiredCols = 1; desiredSize.width = fw*desiredCols + iw; } diff --git a/src/MacVim/MMFullScreenWindow.m b/src/MacVim/MMFullScreenWindow.m index 415c1fbd00..3d532b0d7c 100644 --- a/src/MacVim/MMFullScreenWindow.m +++ b/src/MacVim/MMFullScreenWindow.m @@ -136,6 +136,15 @@ - (void)enterFullScreen { ASLogDebug(@"Enter full-screen now"); + // Detach the window delegate right now to prevent any stray window + // messages (e.g. it may get resized when setting presentationOptions + // below) being sent to the window controller while we are in the middle of + // setting up the full screen window. + NSWindowController *winController = [target windowController]; + id delegate = [target delegate]; + [winController setWindow:nil]; + [target setDelegate:nil]; + // Hide Dock and menu bar when going to full screen. Only do so if the current screen // has a menu bar and dock. if ([self screenHasDockAndMenu]) { @@ -162,13 +171,6 @@ - (void)enterFullScreen // this call so set the frame again just in case. [self setFrame:[[target screen] frame] display:NO]; - // fool delegate - id delegate = [target delegate]; - [target setDelegate:nil]; - - // make target's window controller believe that it's now controlling us - [[target windowController] setWindow:self]; - oldTabBarStyle = [[view tabBarControl] styleName]; NSString *style = @@ -181,7 +183,7 @@ - (void)enterFullScreen [view removeFromSuperviewWithoutNeedingDisplay]; [[self contentView] addSubview:view]; [self setInitialFirstResponder:[view textView]]; - + // NOTE: Calling setTitle:nil causes an exception to be raised (and it is // possible that 'target' has no title when we get here). if ([target title]) { @@ -196,8 +198,10 @@ - (void)enterFullScreen [self setOpaque:[target isOpaque]]; + // reassign target's window controller to believe that it's now controlling us // don't set this sooner, so we don't get an additional - // focus gained message + // focus gained message + [winController setWindow:self]; [self setDelegate:delegate]; // Store view dimension used before entering full-screen, then resize the diff --git a/src/MacVim/MMTextStorage.m b/src/MacVim/MMTextStorage.m index b7bea86908..a8e8c39121 100644 --- a/src/MacVim/MMTextStorage.m +++ b/src/MacVim/MMTextStorage.m @@ -895,6 +895,9 @@ - (NSSize)fitToSize:(NSSize)size rows:(int *)rows columns:(int *)columns if (fh < 1.0f) fh = 1.0f; fitRows = floor(size.height/fh); + // Sanity checking in case unusual window sizes lead to degenerate results + if (fitRows < 1) + fitRows = 1; fitSize.height = fh*fitRows; } @@ -903,6 +906,9 @@ - (NSSize)fitToSize:(NSSize)size rows:(int *)rows columns:(int *)columns if (fw < 1.0f) fw = 1.0f; fitCols = floor(size.width/fw); + // Sanity checking in case unusual window sizes lead to degenerate results + if (fitCols < 1) + fitCols = 1; fitSize.width = fw*fitCols; } diff --git a/src/MacVim/MMWindowController.m b/src/MacVim/MMWindowController.m index 0e389bd6c5..35c9691625 100644 --- a/src/MacVim/MMWindowController.m +++ b/src/MacVim/MMWindowController.m @@ -1307,9 +1307,7 @@ - (void)windowDidResize:(id)sender // Calling setFrameSizeKeepGUISize: instead of setFrameSize: prevents a // degenerate case where frameSizeMayHaveChanged: ends up resizing the window // *again* causing windowDidResize: to be called. - if (fullScreenWindow == nil) { - [vimView setFrameSizeKeepGUISize:[self contentSize]]; - } else { + if (fullScreenEnabled && fullScreenWindow != nil) { // Non-native full screen mode is more complicated and needs to // re-layout the Vim view to properly account for the menu bar / notch, // and misc fuopt configuration. @@ -1318,6 +1316,9 @@ - (void)windowDidResize:(id)sender [vimView setFrameOrigin:desiredFrame.origin]; [vimView setFrameSizeKeepGUISize:desiredFrame.size]; } + else { + [vimView setFrameSizeKeepGUISize:[self contentSize]]; + } } - (void)windowDidChangeBackingProperties:(NSNotification *)notification From 762a8c8f62ddfcb1da5715acdce3a42d76250ec6 Mon Sep 17 00:00:00 2001 From: Yee Cheng Chin Date: Mon, 6 Jan 2025 19:27:21 -0800 Subject: [PATCH 3/3] Fix non-native full screen misc background color and transparency issues Fixed misc bugs: - Previously, setting a colorscheme while in non-native full screen would override the background color specified in 'fuoptions' (default is black). It also wouldn't update the specified color if it's specified to a highlight group (e.g. `set fuopts=background:Normal`). - Transparency and background colors and setting colorscheme also didn't work well togehter. Changing a background color while in fullscreen would reset transparency back to 0. Also, 'fuoptions' can now specify background color in 6-digit hex number now, in addition to the old 8-digit format. Also update docs to elaborate that when using 8-digit, the alpha component is ignored (since we use the 'transparency' setting instead). Add new tests to test 'fuoptions' work. --- runtime/doc/options.txt | 16 ++-- src/MacVim/MMBackend.m | 2 +- src/MacVim/MMVimController.m | 2 +- src/MacVim/MMWindowController.m | 35 ++++++--- src/MacVim/MacVimTests/MacVimTests.m | 112 +++++++++++++++++++++++++++ src/MacVim/gui_macvim.m | 8 ++ src/optionstr.c | 8 +- 7 files changed, 161 insertions(+), 22 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 8b47fc108f..4731409d05 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -3981,15 +3981,19 @@ A jump table for the options with a short description can be found at |Q_op|. of columns fitting on the screen in fullscreen mode. If unset, 'columns' will be unchanged when entering fullscreen mode. background:color - When entering fullscreen, 'color' defines the color of the part - of the screen that is not occupied by the Vim control. If - 'color' is an 8-digit hexadecimal number preceded by '#', - it is interpreted as an explicit color value '#aarrggbb', with - one byte each for the alpha, red, green, and blue values. - Otherwise, 'color' is interpreted as a highlight group name, + When entering fullscreen, "color" defines the color of the part + of the screen that is not occupied by the Vim control, + including the "notch" area for MacBook laptops. + If "color" is a 6 or 8-digit hexadecimal number preceded by + '#', it is interpreted as an explicit color value '#rrggbb' / + '#aarrggbb', with one byte each for the alpha, red, green, and + blue values. The alpha value is ignored (use 'transparency' + instead for a translucent window). + Otherwise, "color" is interpreted as a highlight group name, and the fullscreen background is filled with that highlight group's background color, as defined by the current color scheme. + If unset, the default value is black (#FF000000). Examples: Don't change size when entering fullscreen: > diff --git a/src/MacVim/MMBackend.m b/src/MacVim/MMBackend.m index 523cd5d372..569d4c7c98 100644 --- a/src/MacVim/MMBackend.m +++ b/src/MacVim/MMBackend.m @@ -1188,7 +1188,7 @@ - (void)leaveFullScreen - (void)setFullScreenBackgroundColor:(int)color { NSMutableData *data = [NSMutableData data]; - color = MM_COLOR(color); + color = MM_COLOR_WITH_TRANSP(color,p_transp); [data appendBytes:&color length:sizeof(int)]; [self queueMessage:SetFullScreenColorMsgID data:data]; diff --git a/src/MacVim/MMVimController.m b/src/MacVim/MMVimController.m index ef23eec78b..a1ffcc2897 100644 --- a/src/MacVim/MMVimController.m +++ b/src/MacVim/MMVimController.m @@ -1129,7 +1129,7 @@ - (void)handleMessage:(int)msgid data:(NSData *)data case SetFullScreenColorMsgID: { const int *bg = (const int*)[data bytes]; - NSColor *color = [NSColor colorWithRgbInt:*bg]; + NSColor *color = [NSColor colorWithArgbInt:*bg]; [windowController setFullScreenBackgroundColor:color]; } diff --git a/src/MacVim/MMWindowController.m b/src/MacVim/MMWindowController.m index 35c9691625..c3f2d8bd16 100644 --- a/src/MacVim/MMWindowController.m +++ b/src/MacVim/MMWindowController.m @@ -699,25 +699,32 @@ - (void)setDefaultColorsBackground:(NSColor *)back foreground:(NSColor *)fore // window, so we need to set a transparency color here to make the // transparency show through. if ([back alphaComponent] == 1) { - // Here, any solid color would do, but setting it with "back" has an - // interesting effect where the title bar gets subtly tinted by it - // as well, so do that. (Note that this won't play well in <=10.12 - // since we are using the deprecated - // NSWindowStyleMaskTexturedBackground which makes the titlebars - // transparent in those. Consider not using textured background.) + // The window's background color affects the title bar tint and + // if we are using a transparent title bar this color will show + // up as well. + // (Note that this won't play well in <=10.12 since we are using + // the deprecated NSWindowStyleMaskTexturedBackground which makes + // the titlebars transparent in those. Consider not using textured + // background.) [decoratedWindow setBackgroundColor:back]; + + // Note: We leave the full screen window's background color alone + // because it is affected by 'fuoptions' instead. We just change the + // alpha back to 1 in case it was changed previously because transparency + // was set. if (fullScreenWindow) { - [fullScreenWindow setBackgroundColor:back]; + [fullScreenWindow setBackgroundColor: + [fullScreenWindow.backgroundColor colorWithAlphaComponent:1]]; } } else { // HACK! We really want a transparent background color to avoid // double blending the transparency, but setting alpha=0 leads to // the window border disappearing and also drag-to-resize becomes a // lot slower. So hack around it by making it virtually transparent. - NSColor *clearColor = [back colorWithAlphaComponent:0.001]; - [decoratedWindow setBackgroundColor:clearColor]; + [decoratedWindow setBackgroundColor:[back colorWithAlphaComponent:0.001]]; if (fullScreenWindow) { - [fullScreenWindow setBackgroundColor:clearColor]; + [fullScreenWindow setBackgroundColor: + [fullScreenWindow.backgroundColor colorWithAlphaComponent:0.001]]; } } } @@ -1046,9 +1053,17 @@ - (void)leaveFullScreen } } +/// Called when the window is in non-native full-screen mode and the user has +/// updated the background color. - (void)setFullScreenBackgroundColor:(NSColor *)back { if (fullScreenWindow) + // See setDefaultColorsBackground: for why set a transparent + // background color, and why 0.001 instead of 0. + if ([back alphaComponent] != 1) { + back = [back colorWithAlphaComponent:0.001]; + } + [fullScreenWindow setBackgroundColor:back]; } diff --git a/src/MacVim/MacVimTests/MacVimTests.m b/src/MacVim/MacVimTests/MacVimTests.m index e40d5c4fa3..91cc3da78a 100644 --- a/src/MacVim/MacVimTests/MacVimTests.m +++ b/src/MacVim/MacVimTests/MacVimTests.m @@ -737,6 +737,8 @@ - (void) testWindowResize { [self waitForVimClose]; } +#pragma mark Full screen tests + - (void)waitForNativeFullscreenEnter { XCTestExpectation *expectation = [self expectationWithDescription:@"NativeFullscreenEnter"]; @@ -862,4 +864,114 @@ - (void) testFullScreenNative { [self fullScreenTestWithNative:YES]; } +- (void) testFullScreenNonNativeOptions { + MMAppController *app = MMAppController.sharedInstance; + + // Cache test defaults + NSUserDefaults *ud = NSUserDefaults.standardUserDefaults; + NSDictionary *defaults = [ud volatileDomainForName:NSArgumentDomain]; + NSMutableDictionary *newDefaults = [defaults mutableCopy]; + + // Change native full screen setting + newDefaults[MMNativeFullScreenKey] = @NO; + [ud setVolatileDomain:newDefaults forName:NSArgumentDomain]; + + [app openNewWindow:NewWindowClean activate:YES]; + [self waitForVimOpenAndMessages]; + + MMWindowController *winController = app.keyVimController.windowController; + MMTextView *textView = [[winController vimView] textView]; + + // Test maxvert/maxhorz + [self sendStringToVim:@":set lines=10\n" withMods:0]; + [self sendStringToVim:@":set columns=30\n" withMods:0]; + [self sendStringToVim:@":set fuoptions=\n" withMods:0]; + [self waitForVimProcess]; + + [self sendStringToVim:@":set fu\n" withMods:0]; + [self waitForEventHandlingAndVimProcess]; + [self waitForEventHandlingAndVimProcess]; + XCTAssertEqual(textView.maxRows, 10); + XCTAssertEqual(textView.maxColumns, 30); + [self sendStringToVim:@":set nofu\n" withMods:0]; + [self sendStringToVim:@":set fuoptions=maxvert\n" withMods:0]; + [self sendStringToVim:@":set fu\n" withMods:0]; + [self waitForEventHandlingAndVimProcess]; + [self waitForEventHandlingAndVimProcess]; + XCTAssertGreaterThan(textView.maxRows, 10); + XCTAssertEqual(textView.maxColumns, 30); + [self sendStringToVim:@":set nofu\n" withMods:0]; + [self sendStringToVim:@":set fuoptions=maxhorz\n" withMods:0]; + [self sendStringToVim:@":set fu\n" withMods:0]; + [self waitForEventHandlingAndVimProcess]; + [self waitForEventHandlingAndVimProcess]; + XCTAssertEqual(textView.maxRows, 10); + XCTAssertGreaterThan(textView.maxColumns, 30); + [self sendStringToVim:@":set nofu\n" withMods:0]; + [self sendStringToVim:@":set fuoptions=maxhorz,maxvert\n" withMods:0]; + [self sendStringToVim:@":set fu\n" withMods:0]; + [self waitForEventHandlingAndVimProcess]; + [self waitForEventHandlingAndVimProcess]; + XCTAssertGreaterThan(textView.maxRows, 10); + XCTAssertGreaterThan(textView.maxColumns, 30); + + // Test background color + XCTAssertEqualObjects(winController.window.backgroundColor, [NSColor colorWithArgbInt:0xff000000]); // default is black + + // Make sure changing colorscheme doesn't override the background color unlike in non-full screen mode + [self sendStringToVim:@":color desert\n" withMods:0]; + [self waitForEventHandlingAndVimProcess]; + XCTAssertEqualObjects(winController.window.backgroundColor, [NSColor colorWithArgbInt:0xff000000]); + + // Changing fuoptions should update the background color immediately + [self sendStringToVim:@":set fuoptions=background:Normal\n" withMods:0]; + [self waitForEventHandlingAndVimProcess]; + XCTAssertEqualObjects(winController.window.backgroundColor, [NSColor colorWithArgbInt:0xff333333]); + + // And switching colorscheme should also update the color as well + [self sendStringToVim:@":color blue\n" withMods:0]; + [self waitForEventHandlingAndVimProcess]; + XCTAssertEqualObjects(winController.window.backgroundColor, [NSColor colorWithArgbInt:0xff000087]); + + // Test parsing manual colors in both 8-digit mode (alpha is ignored) and 6-digit mode + [self sendStringToVim:@":set fuoptions=background:#11234567\n" withMods:0]; + [self waitForEventHandlingAndVimProcess]; + XCTAssertEqualObjects(winController.window.backgroundColor, [NSColor colorWithArgbInt:0xff234567]); + [self sendStringToVim:@":set fuoptions=background:#abcdef\n" withMods:0]; + [self waitForEventHandlingAndVimProcess]; + XCTAssertEqualObjects(winController.window.backgroundColor, [NSColor colorWithArgbInt:0xffabcdef]); + + // Test setting transparency while in full screen. We always set the alpha of the background color to 0.001 when transparency is set. + [self sendStringToVim:@":set fuoptions=background:#ffff00\n:set transparency=50\n" withMods:0]; + [self waitForEventHandlingAndVimProcess]; + XCTAssertEqualObjects(winController.window.backgroundColor, [NSColor colorWithRed:1 green:1 blue:0 alpha:0.001]); + + [self sendStringToVim:@":set fuoptions=background:#00ff00\n" withMods:0]; + [self waitForEventHandlingAndVimProcess]; + XCTAssertEqualObjects(winController.window.backgroundColor, [NSColor colorWithRed:0 green:1 blue:0 alpha:0.001]); + + [self sendStringToVim:@":set transparency=0\n" withMods:0]; + [self waitForEventHandlingAndVimProcess]; + XCTAssertEqualObjects(winController.window.backgroundColor, [NSColor colorWithRed:0 green:1 blue:0 alpha:1]); + + // Test setting transparency outside of full screen and make sure it still works + [self sendStringToVim:@":set nofu\n" withMods:0]; + [self waitForEventHandlingAndVimProcess]; + [self waitForEventHandlingAndVimProcess]; + [self sendStringToVim:@":set transparency=50 fuoptions=background:#0000ff\n" withMods:0]; + [self sendStringToVim:@":set fu\n" withMods:0]; + [self waitForEventHandlingAndVimProcess]; + [self waitForEventHandlingAndVimProcess]; + XCTAssertEqualObjects(winController.window.backgroundColor, [NSColor colorWithRed:0 green:0 blue:1 alpha:0.001]); + + // Clean up + [[app keyVimController] sendMessage:VimShouldCloseMsgID data:nil]; + [self waitForVimClose]; + + XCTAssertEqual(0, [app vimControllers].count); + + // Restore settings to test defaults + [ud setVolatileDomain:defaults forName:NSArgumentDomain]; +} + @end diff --git a/src/MacVim/gui_macvim.m b/src/MacVim/gui_macvim.m index 7704ccb978..3538acbbae 100644 --- a/src/MacVim/gui_macvim.m +++ b/src/MacVim/gui_macvim.m @@ -633,6 +633,14 @@ ASLogDebug(@"back=%ld norm=%ld", gui.def_back_pixel, gui.def_norm_pixel); + // If using a highlight group for fullscreen background color we need to + // update the app when a new color scheme has been picked. This function + // technically wouldn't be called if a user manually set the relevant + // highlight group to another color but works in most use cases when they + // just change the color scheme. + if (fuoptions_flags & FUOPT_BGCOLOR_HLGROUP) + gui_mch_fuopt_update(); + [[MMBackend sharedInstance] setDefaultColorsBackground:gui.def_back_pixel foreground:gui.def_norm_pixel]; diff --git a/src/optionstr.c b/src/optionstr.c index c5b1511f6d..b3cfd3a5f9 100644 --- a/src/optionstr.c +++ b/src/optionstr.c @@ -4839,16 +4839,16 @@ check_fuoptions(void) return FAIL; if (p[i] == '#') { - // explicit color (#aarrggbb) + // explicit color (#aarrggbb or #rrggbb) i++; for (j = i; j < i + 8 && vim_isxdigit(p[j]); ++j) ; - if (j < i + 8) - return FAIL; // less than 8 digits + if (j != (i + 8) && j != (i + 6)) + return FAIL; // has to be either 6 or 8 digits if (p[j] != NUL && p[j] != ',') return FAIL; new_fuoptions_bgcolor = 0; - for (k = 0; k < 8; ++k) + for (k = 0; k < (j - i); ++k) new_fuoptions_bgcolor = new_fuoptions_bgcolor * 16 + hex2nr(p[i + k]); i = j;