mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-09-05 19:08:17 +00:00
Compare commits
6 Commits
6530107e3b
...
5013d028a3
Author | SHA1 | Date | |
---|---|---|---|
![]() |
5013d028a3 | ||
![]() |
adfc93047c | ||
![]() |
a3f4997fbc | ||
![]() |
56d3fd872e | ||
![]() |
58e85bf133 | ||
![]() |
19a27383f8 |
@@ -13,7 +13,7 @@ enum QuickTerminalPosition : String {
|
||||
guard let screen = window.screen ?? NSScreen.main else { return }
|
||||
window.setFrame(.init(
|
||||
origin: window.frame.origin,
|
||||
size: size.calculate(position: self, screenDimensions: screen.frame.size)
|
||||
size: size.calculate(position: self, screenDimensions: screen.visibleFrame.size)
|
||||
), display: false)
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ enum QuickTerminalPosition : String {
|
||||
|
||||
/// Get the configured frame size for initial positioning and animations.
|
||||
func configuredFrameSize(on screen: NSScreen, terminalSize: QuickTerminalSize) -> NSSize {
|
||||
let dimensions = terminalSize.calculate(position: self, screenDimensions: screen.frame.size)
|
||||
let dimensions = terminalSize.calculate(position: self, screenDimensions: screen.visibleFrame.size)
|
||||
return NSSize(width: dimensions.width, height: dimensions.height)
|
||||
}
|
||||
|
||||
@@ -66,16 +66,24 @@ enum QuickTerminalPosition : String {
|
||||
func initialOrigin(for window: NSWindow, on screen: NSScreen) -> CGPoint {
|
||||
switch (self) {
|
||||
case .top:
|
||||
return .init(x: round(screen.frame.origin.x + (screen.frame.width - window.frame.width) / 2), y: screen.frame.maxY)
|
||||
return .init(
|
||||
x: round(screen.visibleFrame.origin.x + (screen.visibleFrame.width - window.frame.width) / 2),
|
||||
y: screen.visibleFrame.maxY)
|
||||
|
||||
case .bottom:
|
||||
return .init(x: round(screen.frame.origin.x + (screen.frame.width - window.frame.width) / 2), y: -window.frame.height)
|
||||
return .init(
|
||||
x: round(screen.visibleFrame.origin.x + (screen.visibleFrame.width - window.frame.width) / 2),
|
||||
y: -window.frame.height)
|
||||
|
||||
case .left:
|
||||
return .init(x: screen.frame.minX-window.frame.width, y: round(screen.frame.origin.y + (screen.frame.height - window.frame.height) / 2))
|
||||
return .init(
|
||||
x: screen.visibleFrame.minX-window.frame.width,
|
||||
y: round(screen.visibleFrame.origin.y + (screen.visibleFrame.height - window.frame.height) / 2))
|
||||
|
||||
case .right:
|
||||
return .init(x: screen.frame.maxX, y: round(screen.frame.origin.y + (screen.frame.height - window.frame.height) / 2))
|
||||
return .init(
|
||||
x: screen.visibleFrame.maxX,
|
||||
y: round(screen.visibleFrame.origin.y + (screen.visibleFrame.height - window.frame.height) / 2))
|
||||
|
||||
case .center:
|
||||
return .init(x: round(screen.visibleFrame.origin.x + (screen.visibleFrame.width - window.frame.width) / 2), y: screen.visibleFrame.height - window.frame.width)
|
||||
@@ -86,16 +94,24 @@ enum QuickTerminalPosition : String {
|
||||
func finalOrigin(for window: NSWindow, on screen: NSScreen) -> CGPoint {
|
||||
switch (self) {
|
||||
case .top:
|
||||
return .init(x: round(screen.frame.origin.x + (screen.frame.width - window.frame.width) / 2), y: screen.visibleFrame.maxY - window.frame.height)
|
||||
return .init(
|
||||
x: round(screen.visibleFrame.origin.x + (screen.visibleFrame.width - window.frame.width) / 2),
|
||||
y: screen.visibleFrame.maxY - window.frame.height)
|
||||
|
||||
case .bottom:
|
||||
return .init(x: round(screen.frame.origin.x + (screen.frame.width - window.frame.width) / 2), y: screen.frame.minY)
|
||||
return .init(
|
||||
x: round(screen.visibleFrame.origin.x + (screen.visibleFrame.width - window.frame.width) / 2),
|
||||
y: screen.visibleFrame.minY)
|
||||
|
||||
case .left:
|
||||
return .init(x: screen.frame.minX, y: round(screen.frame.origin.y + (screen.frame.height - window.frame.height) / 2))
|
||||
return .init(
|
||||
x: screen.visibleFrame.minX,
|
||||
y: round(screen.visibleFrame.origin.y + (screen.visibleFrame.height - window.frame.height) / 2))
|
||||
|
||||
case .right:
|
||||
return .init(x: screen.visibleFrame.maxX - window.frame.width, y: round(screen.frame.origin.y + (screen.frame.height - window.frame.height) / 2))
|
||||
return .init(
|
||||
x: screen.visibleFrame.maxX - window.frame.width,
|
||||
y: round(screen.visibleFrame.origin.y + (screen.visibleFrame.height - window.frame.height) / 2))
|
||||
|
||||
case .center:
|
||||
return .init(x: round(screen.visibleFrame.origin.x + (screen.visibleFrame.width - window.frame.width) / 2), y: round(screen.visibleFrame.origin.y + (screen.visibleFrame.height - window.frame.height) / 2))
|
||||
@@ -125,13 +141,13 @@ enum QuickTerminalPosition : String {
|
||||
switch self {
|
||||
case .top:
|
||||
return CGPoint(
|
||||
x: round(screen.frame.origin.x + (screen.frame.width - window.frame.width) / 2),
|
||||
x: round(screen.visibleFrame.origin.x + (screen.visibleFrame.width - window.frame.width) / 2),
|
||||
y: window.frame.origin.y // Keep the same Y position
|
||||
)
|
||||
|
||||
case .bottom:
|
||||
return CGPoint(
|
||||
x: round(screen.frame.origin.x + (screen.frame.width - window.frame.width) / 2),
|
||||
x: round(screen.visibleFrame.origin.x + (screen.visibleFrame.width - window.frame.width) / 2),
|
||||
y: window.frame.origin.y // Keep the same Y position
|
||||
)
|
||||
|
||||
@@ -153,13 +169,13 @@ enum QuickTerminalPosition : String {
|
||||
case .left:
|
||||
return CGPoint(
|
||||
x: window.frame.origin.x, // Keep the same X position
|
||||
y: round(screen.frame.origin.y + (screen.frame.height - window.frame.height) / 2)
|
||||
y: round(screen.visibleFrame.origin.y + (screen.visibleFrame.height - window.frame.height) / 2)
|
||||
)
|
||||
|
||||
case .right:
|
||||
return CGPoint(
|
||||
x: window.frame.origin.x, // Keep the same X position
|
||||
y: round(screen.frame.origin.y + (screen.frame.height - window.frame.height) / 2)
|
||||
y: round(screen.visibleFrame.origin.y + (screen.visibleFrame.height - window.frame.height) / 2)
|
||||
)
|
||||
|
||||
case .top, .bottom, .center:
|
||||
|
@@ -193,7 +193,7 @@ pub const Action = union(enum) {
|
||||
/// Maximum number of intermediate characters during parsing. This is
|
||||
/// 4 because we also use the intermediates array for UTF8 decoding which
|
||||
/// can be at most 4 bytes.
|
||||
const MAX_INTERMEDIATE = 4;
|
||||
pub const MAX_INTERMEDIATE = 4;
|
||||
|
||||
/// Maximum number of CSI parameters. This is arbitrary. Practically, the
|
||||
/// only CSI command that uses more than 3 parameters is the SGR command
|
||||
@@ -206,7 +206,7 @@ const MAX_INTERMEDIATE = 4;
|
||||
/// number. I implore TUI authors to not use more than this number of CSI
|
||||
/// params, but I suspect we'll introduce a slow path with heap allocation
|
||||
/// one day.
|
||||
const MAX_PARAMS = 24;
|
||||
pub const MAX_PARAMS = 24;
|
||||
|
||||
/// Current state of the state machine
|
||||
state: State,
|
||||
@@ -949,6 +949,55 @@ test "csi: too many params" {
|
||||
}
|
||||
}
|
||||
|
||||
test "csi: sgr with up to our max parameters" {
|
||||
for (1..MAX_PARAMS + 1) |max| {
|
||||
var p = init();
|
||||
_ = p.next(0x1B);
|
||||
_ = p.next('[');
|
||||
|
||||
for (0..max - 1) |_| {
|
||||
_ = p.next('1');
|
||||
_ = p.next(';');
|
||||
}
|
||||
_ = p.next('2');
|
||||
|
||||
{
|
||||
const a = p.next('H');
|
||||
try testing.expect(p.state == .ground);
|
||||
try testing.expect(a[0] == null);
|
||||
try testing.expect(a[1].? == .csi_dispatch);
|
||||
try testing.expect(a[2] == null);
|
||||
|
||||
const csi = a[1].?.csi_dispatch;
|
||||
try testing.expectEqual(@as(usize, max), csi.params.len);
|
||||
try testing.expectEqual(@as(u16, 2), csi.params[max - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test "csi: sgr beyond our max drops it" {
|
||||
// Has to be +2 for the loops below
|
||||
const max = MAX_PARAMS + 2;
|
||||
|
||||
var p = init();
|
||||
_ = p.next(0x1B);
|
||||
_ = p.next('[');
|
||||
|
||||
for (0..max - 1) |_| {
|
||||
_ = p.next('1');
|
||||
_ = p.next(';');
|
||||
}
|
||||
_ = p.next('2');
|
||||
|
||||
{
|
||||
const a = p.next('H');
|
||||
try testing.expect(p.state == .ground);
|
||||
try testing.expect(a[0] == null);
|
||||
try testing.expect(a[1] == null);
|
||||
try testing.expect(a[2] == null);
|
||||
}
|
||||
}
|
||||
|
||||
test "dcs: XTGETTCAP" {
|
||||
var p = init();
|
||||
_ = p.next(0x1B);
|
||||
|
@@ -134,7 +134,7 @@ pub const Parser = struct {
|
||||
self.idx += 1;
|
||||
return .{ .unknown = .{
|
||||
.full = self.params,
|
||||
.partial = slice[0 .. self.idx - start + 1],
|
||||
.partial = slice[0..@min(self.idx - start + 1, slice.len)],
|
||||
} };
|
||||
},
|
||||
};
|
||||
|
@@ -249,7 +249,7 @@ pub fn Stream(comptime Handler: type) type {
|
||||
// the parser state to ground.
|
||||
0x18, 0x1A => self.parser.state = .ground,
|
||||
// A parameter digit:
|
||||
'0'...'9' => if (self.parser.params_idx < 16) {
|
||||
'0'...'9' => if (self.parser.params_idx < Parser.MAX_PARAMS) {
|
||||
self.parser.param_acc *|= 10;
|
||||
self.parser.param_acc +|= c - '0';
|
||||
// The parser's CSI param action uses param_acc_idx
|
||||
@@ -259,7 +259,7 @@ pub fn Stream(comptime Handler: type) type {
|
||||
self.parser.param_acc_idx |= 1;
|
||||
},
|
||||
// A parameter separator:
|
||||
':', ';' => if (self.parser.params_idx < 16) {
|
||||
':', ';' => if (self.parser.params_idx < Parser.MAX_PARAMS) {
|
||||
self.parser.params[self.parser.params_idx] = self.parser.param_acc;
|
||||
if (c == ':') self.parser.params_sep.set(self.parser.params_idx);
|
||||
self.parser.params_idx += 1;
|
||||
@@ -2601,3 +2601,22 @@ test "stream CSI ? W reset tab stops" {
|
||||
try s.nextSlice("\x1b[?1;2;3W");
|
||||
try testing.expect(s.handler.reset);
|
||||
}
|
||||
|
||||
test "stream: SGR with 17+ parameters for underline color" {
|
||||
const H = struct {
|
||||
attrs: ?sgr.Attribute = null,
|
||||
called: bool = false,
|
||||
|
||||
pub fn setAttribute(self: *@This(), attr: sgr.Attribute) !void {
|
||||
self.attrs = attr;
|
||||
self.called = true;
|
||||
}
|
||||
};
|
||||
|
||||
var s: Stream(H) = .init(.{});
|
||||
|
||||
// Kakoune-style SGR with underline color as 17th parameter
|
||||
// This tests the fix where param 17 was being dropped
|
||||
try s.nextSlice("\x1b[4:3;38;2;51;51;51;48;2;170;170;170;58;2;255;97;136;0m");
|
||||
try testing.expect(s.handler.called);
|
||||
}
|
||||
|
Reference in New Issue
Block a user