mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-20 02:36:22 +03:00
Merge pull request #2163 from jcollie/gtk-desktop-notification-fix
GTK: Fix clicking on desktop notifications
This commit is contained in:
@ -816,6 +816,8 @@ pub fn handleMessage(self: *Surface, msg: Message) !void {
|
|||||||
.renderer_health => |health| self.updateRendererHealth(health),
|
.renderer_health => |health| self.updateRendererHealth(health),
|
||||||
|
|
||||||
.report_color_scheme => try self.reportColorScheme(),
|
.report_color_scheme => try self.reportColorScheme(),
|
||||||
|
|
||||||
|
.present_surface => try self.presentSurface(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4158,6 +4160,14 @@ fn crashThreadState(self: *Surface) crash.sentry.ThreadState {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tell the surface to present itself to the user. This may involve raising the
|
||||||
|
/// window and switching tabs.
|
||||||
|
fn presentSurface(self: *Surface) !void {
|
||||||
|
if (@hasDecl(apprt.Surface, "presentSurface")) {
|
||||||
|
self.rt_surface.presentSurface();
|
||||||
|
} else log.warn("runtime doesn't support presentSurface", .{});
|
||||||
|
}
|
||||||
|
|
||||||
pub const face_ttf = @embedFile("font/res/JetBrainsMono-Regular.ttf");
|
pub const face_ttf = @embedFile("font/res/JetBrainsMono-Regular.ttf");
|
||||||
pub const face_bold_ttf = @embedFile("font/res/JetBrainsMono-Bold.ttf");
|
pub const face_bold_ttf = @embedFile("font/res/JetBrainsMono-Bold.ttf");
|
||||||
pub const face_emoji_ttf = @embedFile("font/res/NotoColorEmoji.ttf");
|
pub const face_emoji_ttf = @embedFile("font/res/NotoColorEmoji.ttf");
|
||||||
|
@ -382,8 +382,8 @@ fn updateConfigErrors(self: *App) !void {
|
|||||||
|
|
||||||
fn syncActionAccelerators(self: *App) !void {
|
fn syncActionAccelerators(self: *App) !void {
|
||||||
try self.syncActionAccelerator("app.quit", .{ .quit = {} });
|
try self.syncActionAccelerator("app.quit", .{ .quit = {} });
|
||||||
try self.syncActionAccelerator("app.open_config", .{ .open_config = {} });
|
try self.syncActionAccelerator("app.open-config", .{ .open_config = {} });
|
||||||
try self.syncActionAccelerator("app.reload_config", .{ .reload_config = {} });
|
try self.syncActionAccelerator("app.reload-config", .{ .reload_config = {} });
|
||||||
try self.syncActionAccelerator("win.toggle_inspector", .{ .inspector = .toggle });
|
try self.syncActionAccelerator("win.toggle_inspector", .{ .inspector = .toggle });
|
||||||
try self.syncActionAccelerator("win.close", .{ .close_surface = {} });
|
try self.syncActionAccelerator("win.close", .{ .close_surface = {} });
|
||||||
try self.syncActionAccelerator("win.new_window", .{ .new_window = {} });
|
try self.syncActionAccelerator("win.new_window", .{ .new_window = {} });
|
||||||
@ -825,17 +825,58 @@ fn gtkActionQuit(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Action sent by the window manager asking us to present a specific surface to
|
||||||
|
/// the user. Usually because the user clicked on a desktop notification.
|
||||||
|
fn gtkActionPresentSurface(
|
||||||
|
_: *c.GSimpleAction,
|
||||||
|
parameter: *c.GVariant,
|
||||||
|
ud: ?*anyopaque,
|
||||||
|
) callconv(.C) void {
|
||||||
|
const self: *App = @ptrCast(@alignCast(ud orelse return));
|
||||||
|
|
||||||
|
// Make sure that we've receiived a u64 from the system.
|
||||||
|
if (c.g_variant_is_of_type(parameter, c.G_VARIANT_TYPE("t")) == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert that u64 to pointer to a core surface.
|
||||||
|
const surface: *CoreSurface = @ptrFromInt(c.g_variant_get_uint64(parameter));
|
||||||
|
|
||||||
|
// Send a message through the core app mailbox rather than presenting the
|
||||||
|
// surface directly so that it can validate that the surface pointer is
|
||||||
|
// valid. We could get an invalid pointer if a desktop notification outlives
|
||||||
|
// a Ghostty instance and a new one starts up, or there are multiple Ghostty
|
||||||
|
// instances running.
|
||||||
|
_ = self.core_app.mailbox.push(
|
||||||
|
.{
|
||||||
|
.surface_message = .{
|
||||||
|
.surface = surface,
|
||||||
|
.message = .{ .present_surface = {} },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.{ .forever = {} },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// This is called to setup the action map that this application supports.
|
/// This is called to setup the action map that this application supports.
|
||||||
/// This should be called only once on startup.
|
/// This should be called only once on startup.
|
||||||
fn initActions(self: *App) void {
|
fn initActions(self: *App) void {
|
||||||
|
// The set of actions. Each action has (in order):
|
||||||
|
// [0] The action name
|
||||||
|
// [1] The callback function
|
||||||
|
// [2] The GVariantType of the parameter
|
||||||
|
//
|
||||||
|
// For action names:
|
||||||
|
// https://docs.gtk.org/gio/type_func.Action.name_is_valid.html
|
||||||
const actions = .{
|
const actions = .{
|
||||||
.{ "quit", >kActionQuit },
|
.{ "quit", >kActionQuit, null },
|
||||||
.{ "open_config", >kActionOpenConfig },
|
.{ "open-config", >kActionOpenConfig, null },
|
||||||
.{ "reload_config", >kActionReloadConfig },
|
.{ "reload-config", >kActionReloadConfig, null },
|
||||||
|
.{ "present-surface", >kActionPresentSurface, c.G_VARIANT_TYPE("t") },
|
||||||
};
|
};
|
||||||
|
|
||||||
inline for (actions) |entry| {
|
inline for (actions) |entry| {
|
||||||
const action = c.g_simple_action_new(entry[0], null);
|
const action = c.g_simple_action_new(entry[0], entry[2]);
|
||||||
defer c.g_object_unref(action);
|
defer c.g_object_unref(action);
|
||||||
_ = c.g_signal_connect_data(
|
_ = c.g_signal_connect_data(
|
||||||
action,
|
action,
|
||||||
@ -871,8 +912,8 @@ fn initMenu(self: *App) void {
|
|||||||
defer c.g_object_unref(section);
|
defer c.g_object_unref(section);
|
||||||
c.g_menu_append_section(menu, null, @ptrCast(@alignCast(section)));
|
c.g_menu_append_section(menu, null, @ptrCast(@alignCast(section)));
|
||||||
c.g_menu_append(section, "Terminal Inspector", "win.toggle_inspector");
|
c.g_menu_append(section, "Terminal Inspector", "win.toggle_inspector");
|
||||||
c.g_menu_append(section, "Open Configuration", "app.open_config");
|
c.g_menu_append(section, "Open Configuration", "app.open-config");
|
||||||
c.g_menu_append(section, "Reload Configuration", "app.reload_config");
|
c.g_menu_append(section, "Reload Configuration", "app.reload-config");
|
||||||
c.g_menu_append(section, "About Ghostty", "win.about");
|
c.g_menu_append(section, "About Ghostty", "win.about");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1144,19 +1144,26 @@ pub fn showDesktopNotification(
|
|||||||
else => title,
|
else => title,
|
||||||
};
|
};
|
||||||
|
|
||||||
const notif = c.g_notification_new(t.ptr);
|
const notification = c.g_notification_new(t.ptr);
|
||||||
defer c.g_object_unref(notif);
|
defer c.g_object_unref(notification);
|
||||||
c.g_notification_set_body(notif, body.ptr);
|
c.g_notification_set_body(notification, body.ptr);
|
||||||
|
|
||||||
const icon = c.g_themed_icon_new("com.mitchellh.ghostty");
|
const icon = c.g_themed_icon_new("com.mitchellh.ghostty");
|
||||||
defer c.g_object_unref(icon);
|
defer c.g_object_unref(icon);
|
||||||
c.g_notification_set_icon(notif, icon);
|
c.g_notification_set_icon(notification, icon);
|
||||||
|
|
||||||
|
const pointer = c.g_variant_new_uint64(@intFromPtr(&self.core_surface));
|
||||||
|
c.g_notification_set_default_action_and_target_value(
|
||||||
|
notification,
|
||||||
|
"app.present-surface",
|
||||||
|
pointer,
|
||||||
|
);
|
||||||
|
|
||||||
const g_app: *c.GApplication = @ptrCast(self.app.app);
|
const g_app: *c.GApplication = @ptrCast(self.app.app);
|
||||||
|
|
||||||
// We set the notification ID to the body content. If the content is the
|
// We set the notification ID to the body content. If the content is the
|
||||||
// same, this notification may replace a previous notification
|
// same, this notification may replace a previous notification
|
||||||
c.g_application_send_notification(g_app, body.ptr, notif);
|
c.g_application_send_notification(g_app, body.ptr, notification);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn showContextMenu(self: *Surface, x: f32, y: f32) void {
|
fn showContextMenu(self: *Surface, x: f32, y: f32) void {
|
||||||
@ -1967,3 +1974,14 @@ fn translateMods(state: c.GdkModifierType) input.Mods {
|
|||||||
if (state & c.GDK_LOCK_MASK != 0) mods.caps_lock = true;
|
if (state & c.GDK_LOCK_MASK != 0) mods.caps_lock = true;
|
||||||
return mods;
|
return mods;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn presentSurface(self: *Surface) void {
|
||||||
|
if (self.container.window()) |window| {
|
||||||
|
if (self.container.tab()) |tab| {
|
||||||
|
if (window.notebook.getTabPosition(tab)) |position|
|
||||||
|
window.notebook.gotoNthTab(position);
|
||||||
|
}
|
||||||
|
c.gtk_window_present(window.window);
|
||||||
|
}
|
||||||
|
self.grabFocus();
|
||||||
|
}
|
||||||
|
@ -61,6 +61,10 @@ pub const Message = union(enum) {
|
|||||||
/// Report the color scheme
|
/// Report the color scheme
|
||||||
report_color_scheme: void,
|
report_color_scheme: void,
|
||||||
|
|
||||||
|
/// Tell the surface to present itself to the user. This may require raising
|
||||||
|
/// a window and switching tabs.
|
||||||
|
present_surface: void,
|
||||||
|
|
||||||
pub const ReportTitleStyle = enum {
|
pub const ReportTitleStyle = enum {
|
||||||
csi_21_t,
|
csi_21_t,
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user