macos: add visible-menu non-native-fullscreen option

This commit is contained in:
Will Pragnell
2023-09-01 20:17:30 -07:00
parent 7408971d6d
commit 86122624e0
8 changed files with 63 additions and 27 deletions

View File

@ -70,6 +70,12 @@ typedef enum {
GHOSTTY_MOUSE_MOMENTUM_MAY_BEGIN,
} ghostty_input_mouse_momentum_e;
typedef enum {
GHOSTTY_NON_NATIVE_FULLSCREEN_FALSE,
GHOSTTY_NON_NATIVE_FULLSCREEN_TRUE,
GHOSTTY_NON_NATIVE_FULLSCREEN_VISIBLE_MENU,
} ghostty_non_native_fullscreen_e;
// This is a packed struct (see src/input/mouse.zig) but the C standard
// afaik doesn't let us reliably define packed structs so we build it up
// from scratch.
@ -257,7 +263,7 @@ typedef void (*ghostty_runtime_new_window_cb)(void *, ghostty_surface_config_s);
typedef void (*ghostty_runtime_close_surface_cb)(void *, bool);
typedef void (*ghostty_runtime_focus_split_cb)(void *, ghostty_split_focus_direction_e);
typedef void (*ghostty_runtime_goto_tab_cb)(void *, int32_t);
typedef void (*ghostty_runtime_toggle_fullscreen_cb)(void *, bool);
typedef void (*ghostty_runtime_toggle_fullscreen_cb)(void *, ghostty_non_native_fullscreen_e);
typedef struct {
void *userdata;

View File

@ -146,7 +146,7 @@ struct PrimaryView: View {
// Check whether we use non-native fullscreen
guard let useNonNativeFullscreenAny = notification.userInfo?[Ghostty.Notification.NonNativeFullscreenKey] else { return }
guard let useNonNativeFullscreen = useNonNativeFullscreenAny as? Bool else { return }
guard let useNonNativeFullscreen = useNonNativeFullscreenAny as? ghostty_non_native_fullscreen_e else { return }
self.fullScreen.toggleFullscreen(window: window, nonNativeFullscreen: useNonNativeFullscreen)
// After toggling fullscreen we need to focus the terminal again.

View File

@ -74,7 +74,7 @@ extension Ghostty {
close_surface_cb: { userdata, processAlive in AppState.closeSurface(userdata, processAlive: processAlive) },
focus_split_cb: { userdata, direction in AppState.focusSplit(userdata, direction: direction) },
goto_tab_cb: { userdata, n in AppState.gotoTab(userdata, n: n) },
toggle_fullscreen_cb: { userdata, nonNativeFullscreen in AppState.toggleFullscreen(userdata, useNonNativeFullscreen: nonNativeFullscreen) }
toggle_fullscreen_cb: { userdata, nonNativeFullscreen in AppState.toggleFullscreen(userdata, nonNativeFullscreen: nonNativeFullscreen) }
)
// Create the ghostty app.
@ -276,14 +276,13 @@ extension Ghostty {
}
}
static func toggleFullscreen(_ userdata: UnsafeMutableRawPointer?, useNonNativeFullscreen: Bool) {
// togo: use non-native fullscreen
static func toggleFullscreen(_ userdata: UnsafeMutableRawPointer?, nonNativeFullscreen: ghostty_non_native_fullscreen_e) {
guard let surface = self.surfaceUserdata(from: userdata) else { return }
NotificationCenter.default.post(
name: Notification.ghosttyToggleFullscreen,
object: surface,
userInfo: [
Notification.NonNativeFullscreenKey: useNonNativeFullscreen,
Notification.NonNativeFullscreenKey: nonNativeFullscreen,
]
)
}

View File

@ -1,4 +1,5 @@
import SwiftUI
import GhosttyKit
class FullScreenHandler {
var previousTabGroup: NSWindowTabGroup?
@ -11,9 +12,10 @@ class FullScreenHandler {
// and then wants to toggle it off
var isInNonNativeFullscreen: Bool = false
func toggleFullscreen(window: NSWindow, nonNativeFullscreen: Bool) {
func toggleFullscreen(window: NSWindow, nonNativeFullscreen: ghostty_non_native_fullscreen_e) {
let useNonNativeFullscreen = nonNativeFullscreen != GHOSTTY_NON_NATIVE_FULLSCREEN_FALSE
if isInFullscreen {
if nonNativeFullscreen || isInNonNativeFullscreen {
if useNonNativeFullscreen || isInNonNativeFullscreen {
leaveFullscreen(window: window)
isInNonNativeFullscreen = false
} else {
@ -21,8 +23,9 @@ class FullScreenHandler {
}
isInFullscreen = false
} else {
if nonNativeFullscreen {
enterFullscreen(window: window)
if useNonNativeFullscreen {
let hideMenu = nonNativeFullscreen != GHOSTTY_NON_NATIVE_FULLSCREEN_VISIBLE_MENU
enterFullscreen(window: window, hideMenu: hideMenu)
isInNonNativeFullscreen = true
} else {
window.toggleFullScreen(nil)
@ -31,7 +34,7 @@ class FullScreenHandler {
}
}
func enterFullscreen(window: NSWindow) {
func enterFullscreen(window: NSWindow, hideMenu: Bool) {
guard let screen = window.screen else { return }
guard let contentView = window.contentView else { return }
@ -41,7 +44,7 @@ class FullScreenHandler {
// Save previous contentViewFrame and screen
previousContentFrame = window.convertToScreen(contentView.frame)
// Change presentation style to hide menu bar and dock
// Change presentation style to hide menu bar and dock if needed
// It's important to do this in two calls, because setting them in a single call guarantees
// that the menu bar will also be hidden on any additional displays (why? nobody knows!)
// When these options are set separately, the menu bar hiding problem will only occur in
@ -52,19 +55,30 @@ class FullScreenHandler {
if (shouldHideDock(screen: screen)) {
NSApp.presentationOptions.insert(.autoHideDock)
}
if (hideMenu) {
NSApp.presentationOptions.insert(.autoHideMenuBar)
}
// This is important: it gives us the full screen, including the
// notch area on MacBooks.
window.styleMask.remove(.titled)
// Set frame to screen size
window.setFrame(screen.frame, display: true)
// Set frame to screen size, accounting for the menu bar if needed
let frame = calculateFullscreenFrame(screenFrame: screen.frame, subtractMenu: !hideMenu)
window.setFrame(frame, display: true)
// Focus window
window.makeKeyAndOrderFront(nil)
}
func calculateFullscreenFrame(screenFrame: NSRect, subtractMenu: Bool)->NSRect {
if (subtractMenu) {
let menuHeight = NSApp.mainMenu?.menuBarHeight ?? 0
return NSMakeRect(screenFrame.minX, screenFrame.minY, screenFrame.width, screenFrame.height - menuHeight)
}
return screenFrame
}
func leaveFullscreen(window: NSWindow) {
guard let previousFrame = previousContentFrame else { return }

View File

@ -137,7 +137,7 @@ const DerivedConfig = struct {
copy_on_select: configpkg.CopyOnSelect,
confirm_close_surface: bool,
mouse_interval: u64,
macos_non_native_fullscreen: bool,
macos_non_native_fullscreen: configpkg.NonNativeFullscreen,
macos_option_as_alt: configpkg.OptionAsAlt,
pub fn init(alloc_gpa: Allocator, config: *const configpkg.Config) !DerivedConfig {

View File

@ -13,7 +13,8 @@ const apprt = @import("../apprt.zig");
const input = @import("../input.zig");
const CoreApp = @import("../App.zig");
const CoreSurface = @import("../Surface.zig");
const Config = @import("../config.zig").Config;
const configpkg = @import("../config.zig");
const Config = configpkg.Config;
const log = std.log.scoped(.embedded_window);
@ -75,7 +76,7 @@ pub const App = struct {
goto_tab: ?*const fn (SurfaceUD, usize) callconv(.C) void = null,
/// Toggle fullscreen for current window.
toggle_fullscreen: ?*const fn (SurfaceUD, bool) callconv(.C) void = null,
toggle_fullscreen: ?*const fn (SurfaceUD, configpkg.NonNativeFullscreen) callconv(.C) void = null,
};
core_app: *CoreApp,
@ -622,7 +623,7 @@ pub const Surface = struct {
func(self.opts.userdata, n);
}
pub fn toggleFullscreen(self: *Surface, nonNativeFullscreen: bool) void {
pub fn toggleFullscreen(self: *Surface, nonNativeFullscreen: configpkg.NonNativeFullscreen) void {
const func = self.app.opts.toggle_fullscreen orelse {
log.info("runtime embedder does not toggle_fullscreen", .{});
return;

View File

@ -10,7 +10,8 @@ const font = @import("../font/main.zig");
const input = @import("../input.zig");
const CoreApp = @import("../App.zig");
const CoreSurface = @import("../Surface.zig");
const Config = @import("../config.zig").Config;
const configpkg = @import("../config.zig");
const Config = configpkg.Config;
pub const c = @cImport({
@cInclude("gtk/gtk.h");
@ -518,7 +519,7 @@ const Window = struct {
}
/// Toggle fullscreen for this window.
fn toggleFullscreen(self: *Window, _: bool) void {
fn toggleFullscreen(self: *Window, _: configpkg.NonNativeFullscreen) void {
const is_fullscreen = c.gtk_window_is_fullscreen(self.window);
if (is_fullscreen == 0) {
c.gtk_window_fullscreen(self.window);
@ -896,7 +897,7 @@ pub const Surface = struct {
c.gtk_widget_show(alert);
}
pub fn toggleFullscreen(self: *Surface, mac_non_native: bool) void {
pub fn toggleFullscreen(self: *Surface, mac_non_native: configpkg.NonNativeFullscreen) void {
self.window.toggleFullscreen(mac_non_native);
}

View File

@ -278,11 +278,17 @@ pub const Config = struct {
/// The default value is "detect".
@"shell-integration": ShellIntegration = .detect,
/// If true, fullscreen mode on macOS will not use the native fullscreen,
/// but make the window fullscreen without animations and using a new space.
/// That's faster than the native fullscreen mode since it doesn't use
/// animations.
@"macos-non-native-fullscreen": bool = false,
/// If anything other than false, fullscreen mode on macOS will not use the
/// native fullscreen, but make the window fullscreen without animations and
/// using a new space. It's faster than the native fullscreen mode since it
/// doesn't use animations.
///
/// Allowable values are:
///
/// * "visible-menu" - Use non-native macOS fullscreen, keep the menu bar visible
/// * "true" - Use non-native macOS fullscreen, hide the menu bar
/// * "false" - Use native macOS fullscreeen
@"macos-non-native-fullscreen": NonNativeFullscreen = .false,
/// If true, the Option key will be treated as Alt. This makes terminal
/// sequences expecting Alt to work properly, but will break Unicode
@ -1075,6 +1081,15 @@ fn equal(comptime T: type, old: T, new: T) bool {
}
}
/// Valid values for macos-non-native-fullscreen
/// c_int because it needs to be extern compatible
/// If this is changed, you must also update ghostty.h
pub const NonNativeFullscreen = enum(c_int) {
false,
true,
@"visible-menu",
};
/// Valid values for macos-option-as-alt.
pub const OptionAsAlt = enum {
false,