mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
Merge pull request #180 from mitchellh/transparent
Background Transparency and Blurring (1337 h4x0r mode 🤪)
This commit is contained in:
@ -276,6 +276,7 @@ void *ghostty_app_userdata(ghostty_app_t);
|
|||||||
ghostty_surface_t ghostty_surface_new(ghostty_app_t, ghostty_surface_config_s*);
|
ghostty_surface_t ghostty_surface_new(ghostty_app_t, ghostty_surface_config_s*);
|
||||||
void ghostty_surface_free(ghostty_surface_t);
|
void ghostty_surface_free(ghostty_surface_t);
|
||||||
ghostty_app_t ghostty_surface_app(ghostty_surface_t);
|
ghostty_app_t ghostty_surface_app(ghostty_surface_t);
|
||||||
|
bool ghostty_surface_transparent(ghostty_surface_t);
|
||||||
void ghostty_surface_refresh(ghostty_surface_t);
|
void ghostty_surface_refresh(ghostty_surface_t);
|
||||||
void ghostty_surface_set_content_scale(ghostty_surface_t, double, double);
|
void ghostty_surface_set_content_scale(ghostty_surface_t, double, double);
|
||||||
void ghostty_surface_set_focus(ghostty_surface_t, bool);
|
void ghostty_surface_set_focus(ghostty_surface_t, bool);
|
||||||
@ -290,6 +291,10 @@ void ghostty_surface_request_close(ghostty_surface_t);
|
|||||||
void ghostty_surface_split(ghostty_surface_t, ghostty_split_direction_e);
|
void ghostty_surface_split(ghostty_surface_t, ghostty_split_direction_e);
|
||||||
void ghostty_surface_split_focus(ghostty_surface_t, ghostty_split_focus_direction_e);
|
void ghostty_surface_split_focus(ghostty_surface_t, ghostty_split_focus_direction_e);
|
||||||
|
|
||||||
|
// APIs I'd like to get rid of eventually but are still needed for now.
|
||||||
|
// Don't use these unless you know what you're doing.
|
||||||
|
void ghostty_set_window_background_blur(ghostty_surface_t, void *);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -197,6 +197,20 @@ extension Ghostty {
|
|||||||
ghostty_surface_set_size(surface, UInt32(scaledSize.width), UInt32(scaledSize.height))
|
ghostty_surface_set_size(surface, UInt32(scaledSize.width), UInt32(scaledSize.height))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func viewDidMoveToWindow() {
|
||||||
|
guard let window = self.window else { return }
|
||||||
|
guard let surface = self.surface else { return }
|
||||||
|
guard ghostty_surface_transparent(surface) else { return }
|
||||||
|
|
||||||
|
// Set the window transparency settings
|
||||||
|
window.isOpaque = false
|
||||||
|
window.hasShadow = false
|
||||||
|
window.backgroundColor = .clear
|
||||||
|
|
||||||
|
// If we have a blur, set the blur
|
||||||
|
ghostty_set_window_background_blur(surface, Unmanaged.passUnretained(window).toOpaque())
|
||||||
|
}
|
||||||
|
|
||||||
override func resignFirstResponder() -> Bool {
|
override func resignFirstResponder() -> Bool {
|
||||||
let result = super.resignFirstResponder()
|
let result = super.resignFirstResponder()
|
||||||
|
|
||||||
|
@ -470,6 +470,11 @@ pub const CAPI = struct {
|
|||||||
return surface.app;
|
return surface.app;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns ture if the surface has transparency set.
|
||||||
|
export fn ghostty_surface_transparent(surface: *Surface) bool {
|
||||||
|
return surface.app.config.@"background-opacity" < 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
/// Tell the surface that it needs to schedule a render
|
/// Tell the surface that it needs to schedule a render
|
||||||
export fn ghostty_surface_refresh(surface: *Surface) void {
|
export fn ghostty_surface_refresh(surface: *Surface) void {
|
||||||
surface.refresh();
|
surface.refresh();
|
||||||
@ -565,4 +570,34 @@ pub const CAPI = struct {
|
|||||||
export fn ghostty_surface_split_focus(ptr: *Surface, direction: input.SplitFocusDirection) void {
|
export fn ghostty_surface_split_focus(ptr: *Surface, direction: input.SplitFocusDirection) void {
|
||||||
ptr.gotoSplit(direction);
|
ptr.gotoSplit(direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the window background blur on macOS to the desired value.
|
||||||
|
/// I do this in Zig as an extern function because I don't know how to
|
||||||
|
/// call these functions in Swift.
|
||||||
|
///
|
||||||
|
/// This uses an undocumented, non-public API because this is what
|
||||||
|
/// every terminal appears to use, including Terminal.app.
|
||||||
|
export fn ghostty_set_window_background_blur(
|
||||||
|
ptr: *Surface,
|
||||||
|
window: *anyopaque,
|
||||||
|
) void {
|
||||||
|
const config = ptr.app.config;
|
||||||
|
|
||||||
|
// Do nothing if we don't have background transparency enabled
|
||||||
|
if (config.@"background-opacity" >= 1.0) return;
|
||||||
|
|
||||||
|
// Do nothing if our blur value is zero
|
||||||
|
if (config.@"background-blur-radius" == 0) return;
|
||||||
|
|
||||||
|
const nswindow = objc.Object.fromId(window);
|
||||||
|
_ = CGSSetWindowBackgroundBlurRadius(
|
||||||
|
CGSDefaultConnectionForThread(),
|
||||||
|
nswindow.msgSend(usize, objc.sel("windowNumber"), .{}),
|
||||||
|
@intCast(config.@"background-blur-radius"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See ghostty_set_window_background_blur
|
||||||
|
extern "c" fn CGSSetWindowBackgroundBlurRadius(*anyopaque, usize, c_int) i32;
|
||||||
|
extern "c" fn CGSDefaultConnectionForThread() *anyopaque;
|
||||||
};
|
};
|
||||||
|
@ -278,7 +278,7 @@ pub const Surface = struct {
|
|||||||
"ghostty",
|
"ghostty",
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
Renderer.glfwWindowHints(),
|
Renderer.glfwWindowHints(&app.config),
|
||||||
) orelse return glfw.mustGetErrorCode();
|
) orelse return glfw.mustGetErrorCode();
|
||||||
errdefer win.destroy();
|
errdefer win.destroy();
|
||||||
|
|
||||||
|
@ -286,6 +286,9 @@ const Window = struct {
|
|||||||
/// The notebook (tab grouping) for this window.
|
/// The notebook (tab grouping) for this window.
|
||||||
notebook: *c.GtkNotebook,
|
notebook: *c.GtkNotebook,
|
||||||
|
|
||||||
|
/// The background CSS for the window (if any).
|
||||||
|
css_window_background: ?[]u8 = null,
|
||||||
|
|
||||||
pub fn init(self: *Window, app: *App) !void {
|
pub fn init(self: *Window, app: *App) !void {
|
||||||
// Set up our own state
|
// Set up our own state
|
||||||
self.* = .{
|
self.* = .{
|
||||||
@ -301,6 +304,27 @@ const Window = struct {
|
|||||||
self.window = gtk_window;
|
self.window = gtk_window;
|
||||||
c.gtk_window_set_title(gtk_window, "Ghostty");
|
c.gtk_window_set_title(gtk_window, "Ghostty");
|
||||||
c.gtk_window_set_default_size(gtk_window, 200, 200);
|
c.gtk_window_set_default_size(gtk_window, 200, 200);
|
||||||
|
|
||||||
|
// Apply background opacity if we have it
|
||||||
|
if (app.config.@"background-opacity" < 1) {
|
||||||
|
var css = try std.fmt.allocPrint(
|
||||||
|
app.core_app.alloc,
|
||||||
|
".window-transparent {{ background-color: rgba(0, 0, 0, {d}); }}",
|
||||||
|
.{app.config.@"background-opacity"},
|
||||||
|
);
|
||||||
|
self.css_window_background = css;
|
||||||
|
|
||||||
|
const display = c.gtk_widget_get_display(@ptrCast(window));
|
||||||
|
const provider = c.gtk_css_provider_new();
|
||||||
|
c.gtk_css_provider_load_from_data(provider, css.ptr, @intCast(css.len));
|
||||||
|
c.gtk_style_context_add_provider_for_display(
|
||||||
|
display,
|
||||||
|
@ptrCast(provider),
|
||||||
|
c.GTK_STYLE_PROVIDER_PRIORITY_APPLICATION,
|
||||||
|
);
|
||||||
|
c.gtk_widget_add_css_class(@ptrCast(window), "window-transparent");
|
||||||
|
}
|
||||||
|
|
||||||
c.gtk_widget_show(window);
|
c.gtk_widget_show(window);
|
||||||
_ = c.g_signal_connect_data(window, "close-request", c.G_CALLBACK(>kCloseRequest), self, null, G_CONNECT_DEFAULT);
|
_ = c.g_signal_connect_data(window, "close-request", c.G_CALLBACK(>kCloseRequest), self, null, G_CONNECT_DEFAULT);
|
||||||
_ = c.g_signal_connect_data(window, "destroy", c.G_CALLBACK(>kDestroy), self, null, G_CONNECT_DEFAULT);
|
_ = c.g_signal_connect_data(window, "destroy", c.G_CALLBACK(>kDestroy), self, null, G_CONNECT_DEFAULT);
|
||||||
@ -326,9 +350,7 @@ const Window = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Window) void {
|
pub fn deinit(self: *Window) void {
|
||||||
// Notify our app we're gone.
|
if (self.css_window_background) |ptr| self.app.core_app.alloc.free(ptr);
|
||||||
// TODO
|
|
||||||
_ = self;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a new tab to this window.
|
/// Add a new tab to this window.
|
||||||
|
@ -144,6 +144,11 @@ fn parseIntoField(
|
|||||||
0,
|
0,
|
||||||
),
|
),
|
||||||
|
|
||||||
|
f64 => try std.fmt.parseFloat(
|
||||||
|
f64,
|
||||||
|
value orelse return error.ValueRequired,
|
||||||
|
),
|
||||||
|
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -298,6 +303,20 @@ test "parseIntoField: unsigned numbers" {
|
|||||||
try testing.expectEqual(@as(u8, 1), data.u8);
|
try testing.expectEqual(@as(u8, 1), data.u8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "parseIntoField: floats" {
|
||||||
|
const testing = std.testing;
|
||||||
|
var arena = ArenaAllocator.init(testing.allocator);
|
||||||
|
defer arena.deinit();
|
||||||
|
const alloc = arena.allocator();
|
||||||
|
|
||||||
|
var data: struct {
|
||||||
|
f64: f64,
|
||||||
|
} = undefined;
|
||||||
|
|
||||||
|
try parseIntoField(@TypeOf(data), alloc, &data, "f64", "1");
|
||||||
|
try testing.expectEqual(@as(f64, 1.0), data.f64);
|
||||||
|
}
|
||||||
|
|
||||||
test "parseIntoField: optional field" {
|
test "parseIntoField: optional field" {
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
var arena = ArenaAllocator.init(testing.allocator);
|
var arena = ArenaAllocator.init(testing.allocator);
|
||||||
|
@ -63,6 +63,24 @@ pub const Config = struct {
|
|||||||
/// The color of the cursor. If this is not set, a default will be chosen.
|
/// The color of the cursor. If this is not set, a default will be chosen.
|
||||||
@"cursor-color": ?Color = null,
|
@"cursor-color": ?Color = null,
|
||||||
|
|
||||||
|
/// The opacity level (opposite of transparency) of the background.
|
||||||
|
/// A value of 1 is fully opaque and a value of 0 is fully transparent.
|
||||||
|
/// A value less than 0 or greater than 1 will be clamped to the nearest
|
||||||
|
/// valid value.
|
||||||
|
///
|
||||||
|
/// Changing this value at runtime (and reloading config) will only
|
||||||
|
/// affect new windows, tabs, and splits.
|
||||||
|
@"background-opacity": f64 = 1.0,
|
||||||
|
|
||||||
|
/// A positive value enables blurring of the background when
|
||||||
|
/// background-opacity is less than 1. The value is the blur radius to
|
||||||
|
/// apply. A value of 20 is reasonable for a good looking blur.
|
||||||
|
/// Higher values will cause strange rendering issues as well as
|
||||||
|
/// performance issues.
|
||||||
|
///
|
||||||
|
/// This is only supported on macOS.
|
||||||
|
@"background-blur-radius": u8 = 0,
|
||||||
|
|
||||||
/// The command to run, usually a shell. If this is not an absolute path,
|
/// The command to run, usually a shell. If this is not an absolute path,
|
||||||
/// it'll be looked up in the PATH. If this is not set, a default will
|
/// it'll be looked up in the PATH. If this is not set, a default will
|
||||||
/// be looked up from your system. The rules for the default lookup are:
|
/// be looked up from your system. The rules for the default lookup are:
|
||||||
@ -754,6 +772,7 @@ pub const Config = struct {
|
|||||||
switch (@typeInfo(T)) {
|
switch (@typeInfo(T)) {
|
||||||
inline .Bool,
|
inline .Bool,
|
||||||
.Int,
|
.Int,
|
||||||
|
.Float,
|
||||||
=> return src,
|
=> return src,
|
||||||
|
|
||||||
.Optional => |info| return try cloneValue(
|
.Optional => |info| return try cloneValue(
|
||||||
@ -879,6 +898,7 @@ fn equal(comptime T: type, old: T, new: T) bool {
|
|||||||
|
|
||||||
inline .Bool,
|
inline .Bool,
|
||||||
.Int,
|
.Int,
|
||||||
|
.Float,
|
||||||
.Enum,
|
.Enum,
|
||||||
=> return old == new,
|
=> return old == new,
|
||||||
|
|
||||||
|
@ -132,6 +132,7 @@ pub const DerivedConfig = struct {
|
|||||||
font_thicken: bool,
|
font_thicken: bool,
|
||||||
cursor_color: ?terminal.color.RGB,
|
cursor_color: ?terminal.color.RGB,
|
||||||
background: terminal.color.RGB,
|
background: terminal.color.RGB,
|
||||||
|
background_opacity: f64,
|
||||||
foreground: terminal.color.RGB,
|
foreground: terminal.color.RGB,
|
||||||
selection_background: ?terminal.color.RGB,
|
selection_background: ?terminal.color.RGB,
|
||||||
selection_foreground: ?terminal.color.RGB,
|
selection_foreground: ?terminal.color.RGB,
|
||||||
@ -143,6 +144,7 @@ pub const DerivedConfig = struct {
|
|||||||
_ = alloc_gpa;
|
_ = alloc_gpa;
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
|
.background_opacity = @max(0, @min(1, config.@"background-opacity")),
|
||||||
.font_thicken = config.@"font-thicken",
|
.font_thicken = config.@"font-thicken",
|
||||||
|
|
||||||
.cursor_color = if (config.@"cursor-color") |col|
|
.cursor_color = if (config.@"cursor-color") |col|
|
||||||
@ -171,11 +173,10 @@ pub const DerivedConfig = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// Returns the hints that we want for this
|
/// Returns the hints that we want for this
|
||||||
pub fn glfwWindowHints() glfw.Window.Hints {
|
pub fn glfwWindowHints(config: *const configpkg.Config) glfw.Window.Hints {
|
||||||
return .{
|
return .{
|
||||||
.client_api = .no_api,
|
.client_api = .no_api,
|
||||||
// .cocoa_graphics_switching = builtin.os.tag == .macos,
|
.transparent_framebuffer = config.@"background-opacity" < 1,
|
||||||
// .cocoa_retina_framebuffer = true,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,7 +197,7 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal {
|
|||||||
const CAMetalLayer = objc.Class.getClass("CAMetalLayer").?;
|
const CAMetalLayer = objc.Class.getClass("CAMetalLayer").?;
|
||||||
const swapchain = CAMetalLayer.msgSend(objc.Object, objc.sel("layer"), .{});
|
const swapchain = CAMetalLayer.msgSend(objc.Object, objc.sel("layer"), .{});
|
||||||
swapchain.setProperty("device", device.value);
|
swapchain.setProperty("device", device.value);
|
||||||
swapchain.setProperty("opaque", true);
|
swapchain.setProperty("opaque", options.config.background_opacity >= 1);
|
||||||
|
|
||||||
// disable v-sync
|
// disable v-sync
|
||||||
swapchain.setProperty("displaySyncEnabled", false);
|
swapchain.setProperty("displaySyncEnabled", false);
|
||||||
@ -628,7 +629,7 @@ pub fn render(
|
|||||||
.red = @as(f32, @floatFromInt(critical.bg.r)) / 255,
|
.red = @as(f32, @floatFromInt(critical.bg.r)) / 255,
|
||||||
.green = @as(f32, @floatFromInt(critical.bg.g)) / 255,
|
.green = @as(f32, @floatFromInt(critical.bg.g)) / 255,
|
||||||
.blue = @as(f32, @floatFromInt(critical.bg.b)) / 255,
|
.blue = @as(f32, @floatFromInt(critical.bg.b)) / 255,
|
||||||
.alpha = 1.0,
|
.alpha = self.config.background_opacity,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -943,30 +944,25 @@ pub fn updateCell(
|
|||||||
fg: terminal.color.RGB,
|
fg: terminal.color.RGB,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// True if this cell is selected
|
||||||
|
// TODO(perf): we can check in advance if selection is in
|
||||||
|
// our viewport at all and not run this on every point.
|
||||||
|
const selected: bool = if (selection) |sel| selected: {
|
||||||
|
const screen_point = (terminal.point.Viewport{
|
||||||
|
.x = x,
|
||||||
|
.y = y,
|
||||||
|
}).toScreen(screen);
|
||||||
|
|
||||||
|
break :selected sel.contains(screen_point);
|
||||||
|
} else false;
|
||||||
|
|
||||||
// The colors for the cell.
|
// The colors for the cell.
|
||||||
const colors: BgFg = colors: {
|
const colors: BgFg = colors: {
|
||||||
// If we have a selection, then we need to check if this
|
// If we are selected, we our colors are just inverted fg/bg
|
||||||
// cell is selected.
|
var selection_res: ?BgFg = if (selected) .{
|
||||||
// TODO(perf): we can check in advance if selection is in
|
.bg = self.config.selection_background orelse self.config.foreground,
|
||||||
// our viewport at all and not run this on every point.
|
.fg = self.config.selection_foreground orelse self.config.background,
|
||||||
var selection_res: ?BgFg = sel_colors: {
|
} else null;
|
||||||
if (selection) |sel| {
|
|
||||||
const screen_point = (terminal.point.Viewport{
|
|
||||||
.x = x,
|
|
||||||
.y = y,
|
|
||||||
}).toScreen(screen);
|
|
||||||
|
|
||||||
// If we are selected, we our colors are just inverted fg/bg
|
|
||||||
if (sel.contains(screen_point)) {
|
|
||||||
break :sel_colors BgFg{
|
|
||||||
.bg = self.config.selection_background orelse self.config.foreground,
|
|
||||||
.fg = self.config.selection_foreground orelse self.config.background,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break :sel_colors null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const res: BgFg = selection_res orelse if (!cell.attrs.inverse) .{
|
const res: BgFg = selection_res orelse if (!cell.attrs.inverse) .{
|
||||||
// In normal mode, background and fg match the cell. We
|
// In normal mode, background and fg match the cell. We
|
||||||
@ -998,11 +994,37 @@ pub fn updateCell(
|
|||||||
|
|
||||||
// If the cell has a background, we always draw it.
|
// If the cell has a background, we always draw it.
|
||||||
if (colors.bg) |rgb| {
|
if (colors.bg) |rgb| {
|
||||||
|
// Determine our background alpha. If we have transparency configured
|
||||||
|
// then this is dynamic depending on some situations. This is all
|
||||||
|
// in an attempt to make transparency look the best for various
|
||||||
|
// situations. See inline comments.
|
||||||
|
const bg_alpha: u8 = bg_alpha: {
|
||||||
|
if (self.config.background_opacity >= 1) break :bg_alpha alpha;
|
||||||
|
|
||||||
|
// If we're selected, we do not apply background opacity
|
||||||
|
if (selected) break :bg_alpha alpha;
|
||||||
|
|
||||||
|
// If we're reversed, do not apply background opacity
|
||||||
|
if (cell.attrs.inverse) break :bg_alpha alpha;
|
||||||
|
|
||||||
|
// If we have a background and its not the default background
|
||||||
|
// then we apply background opacity
|
||||||
|
if (cell.attrs.has_bg and !std.meta.eql(rgb, self.config.background)) {
|
||||||
|
break :bg_alpha alpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We apply background opacity.
|
||||||
|
var bg_alpha: f64 = @floatFromInt(alpha);
|
||||||
|
bg_alpha *= self.config.background_opacity;
|
||||||
|
bg_alpha = @ceil(bg_alpha);
|
||||||
|
break :bg_alpha @intFromFloat(bg_alpha);
|
||||||
|
};
|
||||||
|
|
||||||
self.cells_bg.appendAssumeCapacity(.{
|
self.cells_bg.appendAssumeCapacity(.{
|
||||||
.mode = .bg,
|
.mode = .bg,
|
||||||
.grid_pos = .{ @as(f32, @floatFromInt(x)), @as(f32, @floatFromInt(y)) },
|
.grid_pos = .{ @as(f32, @floatFromInt(x)), @as(f32, @floatFromInt(y)) },
|
||||||
.cell_width = cell.widthLegacy(),
|
.cell_width = cell.widthLegacy(),
|
||||||
.color = .{ rgb.r, rgb.g, rgb.b, alpha },
|
.color = .{ rgb.r, rgb.g, rgb.b, bg_alpha },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,6 +238,7 @@ pub const DerivedConfig = struct {
|
|||||||
font_thicken: bool,
|
font_thicken: bool,
|
||||||
cursor_color: ?terminal.color.RGB,
|
cursor_color: ?terminal.color.RGB,
|
||||||
background: terminal.color.RGB,
|
background: terminal.color.RGB,
|
||||||
|
background_opacity: f64,
|
||||||
foreground: terminal.color.RGB,
|
foreground: terminal.color.RGB,
|
||||||
selection_background: ?terminal.color.RGB,
|
selection_background: ?terminal.color.RGB,
|
||||||
selection_foreground: ?terminal.color.RGB,
|
selection_foreground: ?terminal.color.RGB,
|
||||||
@ -249,6 +250,7 @@ pub const DerivedConfig = struct {
|
|||||||
_ = alloc_gpa;
|
_ = alloc_gpa;
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
|
.background_opacity = @max(0, @min(1, config.@"background-opacity")),
|
||||||
.font_thicken = config.@"font-thicken",
|
.font_thicken = config.@"font-thicken",
|
||||||
|
|
||||||
.cursor_color = if (config.@"cursor-color") |col|
|
.cursor_color = if (config.@"cursor-color") |col|
|
||||||
@ -461,7 +463,7 @@ fn resetCellsLRU(self: *OpenGL) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the hints that we want for this
|
/// Returns the hints that we want for this
|
||||||
pub fn glfwWindowHints() glfw.Window.Hints {
|
pub fn glfwWindowHints(config: *const configpkg.Config) glfw.Window.Hints {
|
||||||
return .{
|
return .{
|
||||||
.context_version_major = 3,
|
.context_version_major = 3,
|
||||||
.context_version_minor = 3,
|
.context_version_minor = 3,
|
||||||
@ -469,6 +471,7 @@ pub fn glfwWindowHints() glfw.Window.Hints {
|
|||||||
.opengl_forward_compat = true,
|
.opengl_forward_compat = true,
|
||||||
.cocoa_graphics_switching = builtin.os.tag == .macos,
|
.cocoa_graphics_switching = builtin.os.tag == .macos,
|
||||||
.cocoa_retina_framebuffer = true,
|
.cocoa_retina_framebuffer = true,
|
||||||
|
.transparent_framebuffer = config.@"background-opacity" < 1,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1059,30 +1062,25 @@ pub fn updateCell(
|
|||||||
fg: terminal.color.RGB,
|
fg: terminal.color.RGB,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// True if this cell is selected
|
||||||
|
// TODO(perf): we can check in advance if selection is in
|
||||||
|
// our viewport at all and not run this on every point.
|
||||||
|
const selected: bool = if (selection) |sel| selected: {
|
||||||
|
const screen_point = (terminal.point.Viewport{
|
||||||
|
.x = x,
|
||||||
|
.y = y,
|
||||||
|
}).toScreen(screen);
|
||||||
|
|
||||||
|
break :selected sel.contains(screen_point);
|
||||||
|
} else false;
|
||||||
|
|
||||||
// The colors for the cell.
|
// The colors for the cell.
|
||||||
const colors: BgFg = colors: {
|
const colors: BgFg = colors: {
|
||||||
// If we have a selection, then we need to check if this
|
// If we are selected, we our colors are just inverted fg/bg
|
||||||
// cell is selected.
|
var selection_res: ?BgFg = if (selected) .{
|
||||||
// TODO(perf): we can check in advance if selection is in
|
.bg = self.config.selection_background orelse self.config.foreground,
|
||||||
// our viewport at all and not run this on every point.
|
.fg = self.config.selection_foreground orelse self.config.background,
|
||||||
var selection_res: ?BgFg = sel_colors: {
|
} else null;
|
||||||
if (selection) |sel| {
|
|
||||||
const screen_point = (terminal.point.Viewport{
|
|
||||||
.x = x,
|
|
||||||
.y = y,
|
|
||||||
}).toScreen(screen);
|
|
||||||
|
|
||||||
// If we are selected, we our colors are just inverted fg/bg
|
|
||||||
if (sel.contains(screen_point)) {
|
|
||||||
break :sel_colors BgFg{
|
|
||||||
.bg = self.config.selection_background orelse self.config.foreground,
|
|
||||||
.fg = self.config.selection_foreground orelse self.config.background,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break :sel_colors null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const res: BgFg = selection_res orelse if (!cell.attrs.inverse) .{
|
const res: BgFg = selection_res orelse if (!cell.attrs.inverse) .{
|
||||||
// In normal mode, background and fg match the cell. We
|
// In normal mode, background and fg match the cell. We
|
||||||
@ -1125,10 +1123,34 @@ pub fn updateCell(
|
|||||||
|
|
||||||
// If the cell has a background, we always draw it.
|
// If the cell has a background, we always draw it.
|
||||||
if (colors.bg) |rgb| {
|
if (colors.bg) |rgb| {
|
||||||
var mode: GPUCellMode = .bg;
|
// Determine our background alpha. If we have transparency configured
|
||||||
|
// then this is dynamic depending on some situations. This is all
|
||||||
|
// in an attempt to make transparency look the best for various
|
||||||
|
// situations. See inline comments.
|
||||||
|
const bg_alpha: u8 = bg_alpha: {
|
||||||
|
if (self.config.background_opacity >= 1) break :bg_alpha alpha;
|
||||||
|
|
||||||
|
// If we're selected, we do not apply background opacity
|
||||||
|
if (selected) break :bg_alpha alpha;
|
||||||
|
|
||||||
|
// If we're reversed, do not apply background opacity
|
||||||
|
if (cell.attrs.inverse) break :bg_alpha alpha;
|
||||||
|
|
||||||
|
// If we have a background and its not the default background
|
||||||
|
// then we apply background opacity
|
||||||
|
if (cell.attrs.has_bg and !std.meta.eql(rgb, self.config.background)) {
|
||||||
|
break :bg_alpha alpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We apply background opacity.
|
||||||
|
var bg_alpha: f64 = @floatFromInt(alpha);
|
||||||
|
bg_alpha *= self.config.background_opacity;
|
||||||
|
bg_alpha = @ceil(bg_alpha);
|
||||||
|
break :bg_alpha @intFromFloat(bg_alpha);
|
||||||
|
};
|
||||||
|
|
||||||
self.cells_bg.appendAssumeCapacity(.{
|
self.cells_bg.appendAssumeCapacity(.{
|
||||||
.mode = mode,
|
.mode = .bg,
|
||||||
.grid_col = @intCast(x),
|
.grid_col = @intCast(x),
|
||||||
.grid_row = @intCast(y),
|
.grid_row = @intCast(y),
|
||||||
.grid_width = cell.widthLegacy(),
|
.grid_width = cell.widthLegacy(),
|
||||||
@ -1145,7 +1167,7 @@ pub fn updateCell(
|
|||||||
.bg_r = rgb.r,
|
.bg_r = rgb.r,
|
||||||
.bg_g = rgb.g,
|
.bg_g = rgb.g,
|
||||||
.bg_b = rgb.b,
|
.bg_b = rgb.b,
|
||||||
.bg_a = alpha,
|
.bg_a = bg_alpha,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1411,7 +1433,7 @@ pub fn draw(self: *OpenGL) !void {
|
|||||||
@as(f32, @floatFromInt(self.draw_background.r)) / 255,
|
@as(f32, @floatFromInt(self.draw_background.r)) / 255,
|
||||||
@as(f32, @floatFromInt(self.draw_background.g)) / 255,
|
@as(f32, @floatFromInt(self.draw_background.g)) / 255,
|
||||||
@as(f32, @floatFromInt(self.draw_background.b)) / 255,
|
@as(f32, @floatFromInt(self.draw_background.b)) / 255,
|
||||||
1.0,
|
@floatCast(self.config.background_opacity),
|
||||||
);
|
);
|
||||||
gl.clear(gl.c.GL_COLOR_BUFFER_BIT);
|
gl.clear(gl.c.GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user