mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
macos: handle multiple monitors properly
This commit is contained in:
@ -21,6 +21,7 @@
|
||||
A51BFC272B30F1B800E92F16 /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = A51BFC262B30F1B800E92F16 /* Sparkle */; };
|
||||
A51BFC2B2B30F6BE00E92F16 /* UpdateDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51BFC2A2B30F6BE00E92F16 /* UpdateDelegate.swift */; };
|
||||
A5278A9B2AA05B2600CD3039 /* Ghostty.Input.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5278A9A2AA05B2600CD3039 /* Ghostty.Input.swift */; };
|
||||
A52FFF572CA90484000C6A5B /* QuickTerminalScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = A52FFF562CA90481000C6A5B /* QuickTerminalScreen.swift */; };
|
||||
A5333E1C2B5A1CE3008AEFF7 /* CrossKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5333E1B2B5A1CE3008AEFF7 /* CrossKit.swift */; };
|
||||
A5333E1D2B5A1CE3008AEFF7 /* CrossKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5333E1B2B5A1CE3008AEFF7 /* CrossKit.swift */; };
|
||||
A5333E202B5A2111008AEFF7 /* SurfaceView_UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5333E152B59DE8E008AEFF7 /* SurfaceView_UIKit.swift */; };
|
||||
@ -103,6 +104,7 @@
|
||||
A51BFC282B30F26D00E92F16 /* GhosttyDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = GhosttyDebug.entitlements; sourceTree = "<group>"; };
|
||||
A51BFC2A2B30F6BE00E92F16 /* UpdateDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateDelegate.swift; sourceTree = "<group>"; };
|
||||
A5278A9A2AA05B2600CD3039 /* Ghostty.Input.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ghostty.Input.swift; sourceTree = "<group>"; };
|
||||
A52FFF562CA90481000C6A5B /* QuickTerminalScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickTerminalScreen.swift; sourceTree = "<group>"; };
|
||||
A5333E152B59DE8E008AEFF7 /* SurfaceView_UIKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurfaceView_UIKit.swift; sourceTree = "<group>"; };
|
||||
A5333E1B2B5A1CE3008AEFF7 /* CrossKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CrossKit.swift; sourceTree = "<group>"; };
|
||||
A5333E212B5A2128008AEFF7 /* SurfaceView_AppKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurfaceView_AppKit.swift; sourceTree = "<group>"; };
|
||||
@ -391,6 +393,7 @@
|
||||
A5CBD05B2CA0C5C70017A1AE /* QuickTerminal.xib */,
|
||||
A5CBD05D2CA0C5E70017A1AE /* QuickTerminalController.swift */,
|
||||
A5CBD0632CA122E70017A1AE /* QuickTerminalPosition.swift */,
|
||||
A52FFF562CA90481000C6A5B /* QuickTerminalScreen.swift */,
|
||||
A5CBD05F2CA0C9080017A1AE /* QuickTerminalWindow.swift */,
|
||||
);
|
||||
path = QuickTerminal;
|
||||
@ -588,6 +591,7 @@
|
||||
A5CDF1932AAF9E0800513312 /* ConfigurationErrorsController.swift in Sources */,
|
||||
A59FB5D12AE0DEA7009128F3 /* MetalView.swift in Sources */,
|
||||
A55685E029A03A9F004303CE /* AppError.swift in Sources */,
|
||||
A52FFF572CA90484000C6A5B /* QuickTerminalScreen.swift in Sources */,
|
||||
A5CC36132C9CD72D004D6760 /* SecureInputOverlay.swift in Sources */,
|
||||
A535B9DA299C569B0017E2E4 /* ErrorView.swift in Sources */,
|
||||
A51BFC202B2FB64F00E92F16 /* AboutController.swift in Sources */,
|
||||
|
@ -66,7 +66,9 @@ class QuickTerminalController: BaseTerminalController {
|
||||
}
|
||||
|
||||
func windowWillResize(_ sender: NSWindow, to frameSize: NSSize) -> NSSize {
|
||||
guard let screen = NSScreen.main else { return frameSize }
|
||||
// We use the actual screen the window is on for this, since it should
|
||||
// be on the proper screen.
|
||||
guard let screen = window?.screen ?? NSScreen.main else { return frameSize }
|
||||
return position.restrictFrameSize(frameSize, on: screen)
|
||||
}
|
||||
|
||||
@ -132,7 +134,7 @@ class QuickTerminalController: BaseTerminalController {
|
||||
}
|
||||
|
||||
private func animateWindowIn(window: NSWindow, from position: QuickTerminalPosition) {
|
||||
guard let screen = NSScreen.main else { return }
|
||||
guard let screen = ghostty.config.quickTerminalScreen.screen else { return }
|
||||
|
||||
// Move our window off screen to the top
|
||||
position.setInitial(in: window, on: screen)
|
||||
@ -150,7 +152,8 @@ class QuickTerminalController: BaseTerminalController {
|
||||
}
|
||||
|
||||
private func animateWindowOut(window: NSWindow, to position: QuickTerminalPosition) {
|
||||
guard let screen = NSScreen.main else { return }
|
||||
// We always animate out to whatever screen the window is actually on.
|
||||
guard let screen = window.screen ?? NSScreen.main else { return }
|
||||
|
||||
// Keep track of if we were the key window. If we were the key window then we
|
||||
// want to move focus to the next window so that focus is preserved somewhere
|
||||
|
@ -36,7 +36,7 @@ enum QuickTerminalPosition : String {
|
||||
// Position depends
|
||||
window.setFrame(.init(
|
||||
origin: initialOrigin(for: window, on: screen),
|
||||
size: window.frame.size
|
||||
size: restrictFrameSize(window.frame.size, on: screen)
|
||||
), display: false)
|
||||
}
|
||||
|
||||
@ -48,7 +48,7 @@ enum QuickTerminalPosition : String {
|
||||
// Position depends
|
||||
window.setFrame(.init(
|
||||
origin: finalOrigin(for: window, on: screen),
|
||||
size: window.frame.size
|
||||
size: restrictFrameSize(window.frame.size, on: screen)
|
||||
), display: true)
|
||||
}
|
||||
|
||||
@ -70,10 +70,10 @@ enum QuickTerminalPosition : String {
|
||||
func initialOrigin(for window: NSWindow, on screen: NSScreen) -> CGPoint {
|
||||
switch (self) {
|
||||
case .top:
|
||||
return .init(x: 0, y: screen.frame.maxY)
|
||||
return .init(x: screen.frame.minX, y: screen.frame.maxY)
|
||||
|
||||
case .bottom:
|
||||
return .init(x: 0, y: -window.frame.height)
|
||||
return .init(x: screen.frame.minX, y: -window.frame.height)
|
||||
|
||||
case .left:
|
||||
return .init(x: -window.frame.width, y: 0)
|
||||
@ -87,13 +87,13 @@ enum QuickTerminalPosition : String {
|
||||
func finalOrigin(for window: NSWindow, on screen: NSScreen) -> CGPoint {
|
||||
switch (self) {
|
||||
case .top:
|
||||
return .init(x: window.frame.origin.x, y: screen.visibleFrame.maxY - window.frame.height)
|
||||
return .init(x: screen.frame.minX, y: screen.visibleFrame.maxY - window.frame.height)
|
||||
|
||||
case .bottom:
|
||||
return .init(x: window.frame.origin.x, y: 0)
|
||||
return .init(x: screen.frame.minX, y: screen.frame.minY)
|
||||
|
||||
case .left:
|
||||
return .init(x: 0, y: window.frame.origin.y)
|
||||
return .init(x: screen.frame.minX, y: window.frame.origin.y)
|
||||
|
||||
case .right:
|
||||
return .init(x: screen.visibleFrame.maxX - window.frame.width, y: window.frame.origin.y)
|
||||
|
@ -0,0 +1,37 @@
|
||||
import Cocoa
|
||||
|
||||
enum QuickTerminalScreen {
|
||||
case main
|
||||
case mouse
|
||||
case menuBar
|
||||
|
||||
init?(fromGhosttyConfig string: String) {
|
||||
switch (string) {
|
||||
case "main":
|
||||
self = .main
|
||||
|
||||
case "mouse":
|
||||
self = .mouse
|
||||
|
||||
case "macos-menu-bar":
|
||||
self = .menuBar
|
||||
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var screen: NSScreen? {
|
||||
switch (self) {
|
||||
case .main:
|
||||
return NSScreen.main
|
||||
|
||||
case .mouse:
|
||||
let mouseLoc = NSEvent.mouseLocation
|
||||
return NSScreen.screens.first(where: { $0.frame.contains(mouseLoc) })
|
||||
|
||||
case .menuBar:
|
||||
return NSScreen.screens.first
|
||||
}
|
||||
}
|
||||
}
|
@ -342,6 +342,16 @@ extension Ghostty {
|
||||
let str = String(cString: ptr)
|
||||
return QuickTerminalPosition(rawValue: str) ?? .top
|
||||
}
|
||||
|
||||
var quickTerminalScreen: QuickTerminalScreen {
|
||||
guard let config = self.config else { return .main }
|
||||
var v: UnsafePointer<Int8>? = nil
|
||||
let key = "quick-terminal-screen"
|
||||
guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return .main }
|
||||
guard let ptr = v else { return .main }
|
||||
let str = String(cString: ptr)
|
||||
return QuickTerminalScreen(fromGhosttyConfig: str) ?? .main
|
||||
}
|
||||
#endif
|
||||
|
||||
var resizeOverlay: ResizeOverlay {
|
||||
|
@ -1234,6 +1234,26 @@ keybind: Keybinds = .{},
|
||||
/// Changing this configuration requires restarting Ghostty completely.
|
||||
@"quick-terminal-position": QuickTerminalPosition = .top,
|
||||
|
||||
/// The screen where the quick terminal should show up.
|
||||
///
|
||||
/// Valid values are:
|
||||
///
|
||||
/// * `main` - The screen that the operating system recommends as the main
|
||||
/// screen. On macOS, this is the screen that is currently receiving
|
||||
/// keyboard input. This screen is defined by the operating system and
|
||||
/// not chosen by Ghostty.
|
||||
///
|
||||
/// * `mouse` - The screen that the mouse is currently hovered over.
|
||||
///
|
||||
/// * `macos-menu-bar` - The screen that contains the macOS menu bar as
|
||||
/// set in the display settings on macOS. This is a bit confusing because
|
||||
/// every screen on macOS has a menu bar, but this is the screen that
|
||||
/// contains the primary menu bar.
|
||||
///
|
||||
/// The default value is `main` because this is the recommended screen
|
||||
/// by the operating system.
|
||||
@"quick-terminal-screen": QuickTerminalScreen = .main,
|
||||
|
||||
/// Whether to enable shell integration auto-injection or not. Shell integration
|
||||
/// greatly enhances the terminal experience by enabling a number of features:
|
||||
///
|
||||
@ -4423,6 +4443,13 @@ pub const QuickTerminalPosition = enum {
|
||||
right,
|
||||
};
|
||||
|
||||
/// See quick-terminal-screen
|
||||
pub const QuickTerminalScreen = enum {
|
||||
main,
|
||||
mouse,
|
||||
@"macos-menu-bar",
|
||||
};
|
||||
|
||||
/// See grapheme-width-method
|
||||
pub const GraphemeWidthMethod = enum {
|
||||
legacy,
|
||||
|
Reference in New Issue
Block a user