mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-09-05 19:08:17 +00:00
gtk: implement bell (#7087)
This PR implements a more lightweight alternative to #5326 that contains features that I personally think Just Make Sense for the bell. No configs, no GStreamer stuff, just sane defaults to get us started.
This commit is contained in:
@@ -601,6 +601,7 @@ typedef enum {
|
||||
GHOSTTY_ACTION_RELOAD_CONFIG,
|
||||
GHOSTTY_ACTION_CONFIG_CHANGE,
|
||||
GHOSTTY_ACTION_CLOSE_WINDOW,
|
||||
GHOSTTY_ACTION_RING_BELL,
|
||||
} ghostty_action_tag_e;
|
||||
|
||||
typedef union {
|
||||
|
@@ -930,6 +930,16 @@ pub fn handleMessage(self: *Surface, msg: Message) !void {
|
||||
.present_surface => try self.presentSurface(),
|
||||
|
||||
.password_input => |v| try self.passwordInput(v),
|
||||
|
||||
.ring_bell => {
|
||||
_ = self.rt_app.performAction(
|
||||
.{ .surface = self },
|
||||
.ring_bell,
|
||||
{},
|
||||
) catch |err| {
|
||||
log.warn("apprt failed to ring bell={}", .{err});
|
||||
};
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -244,6 +244,8 @@ pub const Action = union(Key) {
|
||||
/// Closes the currently focused window.
|
||||
close_window,
|
||||
|
||||
ring_bell,
|
||||
|
||||
/// Sync with: ghostty_action_tag_e
|
||||
pub const Key = enum(c_int) {
|
||||
quit,
|
||||
@@ -287,6 +289,7 @@ pub const Action = union(Key) {
|
||||
reload_config,
|
||||
config_change,
|
||||
close_window,
|
||||
ring_bell,
|
||||
};
|
||||
|
||||
/// Sync with: ghostty_action_u
|
||||
|
@@ -246,6 +246,7 @@ pub const App = struct {
|
||||
.toggle_maximize,
|
||||
.prompt_title,
|
||||
.reset_window_size,
|
||||
.ring_bell,
|
||||
=> {
|
||||
log.info("unimplemented action={}", .{action});
|
||||
return false;
|
||||
|
@@ -484,6 +484,7 @@ pub fn performAction(
|
||||
.prompt_title => try self.promptTitle(target),
|
||||
.toggle_quick_terminal => return try self.toggleQuickTerminal(),
|
||||
.secure_input => self.setSecureInput(target, value),
|
||||
.ring_bell => try self.ringBell(target),
|
||||
|
||||
// Unimplemented
|
||||
.close_all_windows,
|
||||
@@ -775,6 +776,13 @@ fn toggleQuickTerminal(self: *App) !bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
fn ringBell(_: *App, target: apprt.Target) !void {
|
||||
switch (target) {
|
||||
.app => {},
|
||||
.surface => |surface| try surface.rt_surface.ringBell(),
|
||||
}
|
||||
}
|
||||
|
||||
fn quitTimer(self: *App, mode: apprt.action.QuitTimer) void {
|
||||
switch (mode) {
|
||||
.start => self.startQuitTimer(),
|
||||
|
@@ -2439,3 +2439,25 @@ pub fn setSecureInput(self: *Surface, value: apprt.action.SecureInput) void {
|
||||
.toggle => self.is_secure_input = !self.is_secure_input,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ringBell(self: *Surface) !void {
|
||||
const features = self.app.config.@"bell-features";
|
||||
const window = self.container.window() orelse {
|
||||
log.warn("failed to ring bell: surface is not attached to any window", .{});
|
||||
return;
|
||||
};
|
||||
|
||||
// System beep
|
||||
if (features.system) system: {
|
||||
const surface = window.window.as(gtk.Native).getSurface() orelse break :system;
|
||||
surface.beep();
|
||||
}
|
||||
|
||||
// Mark tab as needing attention
|
||||
if (self.container.tab()) |tab| tab: {
|
||||
const page = window.notebook.getTabPage(tab) orelse break :tab;
|
||||
|
||||
// Need attention if we're not the currently selected tab
|
||||
if (page.getSelected() == 0) page.setNeedsAttention(@intFromBool(true));
|
||||
}
|
||||
}
|
||||
|
@@ -114,9 +114,12 @@ pub fn gotoNthTab(self: *TabView, position: c_int) bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn getTabPage(self: *TabView, tab: *Tab) ?*adw.TabPage {
|
||||
return self.tab_view.getPage(tab.box.as(gtk.Widget));
|
||||
}
|
||||
|
||||
pub fn getTabPosition(self: *TabView, tab: *Tab) ?c_int {
|
||||
const page = self.tab_view.getPage(tab.box.as(gtk.Widget));
|
||||
return self.tab_view.getPagePosition(page);
|
||||
return self.tab_view.getPagePosition(self.getTabPage(tab) orelse return null);
|
||||
}
|
||||
|
||||
pub fn gotoPreviousTab(self: *TabView, tab: *Tab) bool {
|
||||
@@ -161,17 +164,16 @@ pub fn moveTab(self: *TabView, tab: *Tab, position: c_int) void {
|
||||
}
|
||||
|
||||
pub fn reorderPage(self: *TabView, tab: *Tab, position: c_int) void {
|
||||
const page = self.tab_view.getPage(tab.box.as(gtk.Widget));
|
||||
_ = self.tab_view.reorderPage(page, position);
|
||||
_ = self.tab_view.reorderPage(self.getTabPage(tab) orelse return, position);
|
||||
}
|
||||
|
||||
pub fn setTabTitle(self: *TabView, tab: *Tab, title: [:0]const u8) void {
|
||||
const page = self.tab_view.getPage(tab.box.as(gtk.Widget));
|
||||
const page = self.getTabPage(tab) orelse return;
|
||||
page.setTitle(title.ptr);
|
||||
}
|
||||
|
||||
pub fn setTabTooltip(self: *TabView, tab: *Tab, tooltip: [:0]const u8) void {
|
||||
const page = self.tab_view.getPage(tab.box.as(gtk.Widget));
|
||||
const page = self.getTabPage(tab) orelse return;
|
||||
page.setTooltip(tooltip.ptr);
|
||||
}
|
||||
|
||||
@@ -203,8 +205,7 @@ pub fn closeTab(self: *TabView, tab: *Tab) void {
|
||||
if (n > 1) self.forcing_close = false;
|
||||
}
|
||||
|
||||
const page = self.tab_view.getPage(tab.box.as(gtk.Widget));
|
||||
self.tab_view.closePage(page);
|
||||
if (self.getTabPage(tab)) |page| self.tab_view.closePage(page);
|
||||
|
||||
// If we have no more tabs we close the window
|
||||
if (self.nPages() == 0) {
|
||||
@@ -260,6 +261,11 @@ fn adwTabViewCreateWindow(
|
||||
|
||||
fn adwSelectPage(_: *adw.TabView, _: *gobject.ParamSpec, self: *TabView) callconv(.C) void {
|
||||
const page = self.tab_view.getSelectedPage() orelse return;
|
||||
|
||||
// If the tab was previously marked as needing attention
|
||||
// (e.g. due to a bell character), we now unmark that
|
||||
page.setNeedsAttention(@intFromBool(false));
|
||||
|
||||
const title = page.getTitle();
|
||||
self.window.setTitle(std.mem.span(title));
|
||||
}
|
||||
|
@@ -81,6 +81,9 @@ pub const Message = union(enum) {
|
||||
/// The terminal has reported a change in the working directory.
|
||||
pwd_change: WriteReq,
|
||||
|
||||
/// The terminal encountered a bell character.
|
||||
ring_bell,
|
||||
|
||||
pub const ReportTitleStyle = enum {
|
||||
csi_21_t,
|
||||
|
||||
|
@@ -1861,6 +1861,22 @@ keybind: Keybinds = .{},
|
||||
/// open terminals.
|
||||
@"custom-shader-animation": CustomShaderAnimation = .true,
|
||||
|
||||
/// The list of enabled features that are activated after encountering
|
||||
/// a bell character.
|
||||
///
|
||||
/// Valid values are:
|
||||
///
|
||||
/// * `system` (default)
|
||||
///
|
||||
/// Instructs the system to notify the user using built-in system functions.
|
||||
/// This could result in an audiovisual effect, a notification, or something
|
||||
/// else entirely. Changing these effects require altering system settings:
|
||||
/// for instance under the "Sound > Alert Sound" setting in GNOME,
|
||||
/// or the "Accessibility > System Bell" settings in KDE Plasma.
|
||||
///
|
||||
/// Currently only implemented on Linux.
|
||||
@"bell-features": BellFeatures = .{},
|
||||
|
||||
/// Control the in-app notifications that Ghostty shows.
|
||||
///
|
||||
/// On Linux (GTK), in-app notifications show up as toasts. Toasts appear
|
||||
@@ -5691,6 +5707,11 @@ pub const AppNotifications = packed struct {
|
||||
@"clipboard-copy": bool = true,
|
||||
};
|
||||
|
||||
/// See bell-features
|
||||
pub const BellFeatures = packed struct {
|
||||
system: bool = false,
|
||||
};
|
||||
|
||||
/// See mouse-shift-capture
|
||||
pub const MouseShiftCapture = enum {
|
||||
false,
|
||||
|
@@ -325,9 +325,8 @@ pub const StreamHandler = struct {
|
||||
try self.terminal.printRepeat(count);
|
||||
}
|
||||
|
||||
pub fn bell(self: StreamHandler) !void {
|
||||
_ = self;
|
||||
log.info("BELL", .{});
|
||||
pub fn bell(self: *StreamHandler) !void {
|
||||
self.surfaceMessageWriter(.ring_bell);
|
||||
}
|
||||
|
||||
pub fn backspace(self: *StreamHandler) !void {
|
||||
|
Reference in New Issue
Block a user