Skip to content

Commit

Permalink
Merge pull request #2163 from jcollie/gtk-desktop-notification-fix
Browse files Browse the repository at this point in the history
GTK: Fix clicking on desktop notifications
  • Loading branch information
mitchellh authored Sep 14, 2024
2 parents 841e12a + 941adcd commit 779b721
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 13 deletions.
10 changes: 10 additions & 0 deletions src/Surface.zig
Original file line number Diff line number Diff line change
Expand Up @@ -816,6 +816,8 @@ pub fn handleMessage(self: *Surface, msg: Message) !void {
.renderer_health => |health| self.updateRendererHealth(health),

.report_color_scheme => try self.reportColorScheme(),

.present_surface => try self.presentSurface(),
}
}

Expand Down Expand Up @@ -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_bold_ttf = @embedFile("font/res/JetBrainsMono-Bold.ttf");
pub const face_emoji_ttf = @embedFile("font/res/NotoColorEmoji.ttf");
Expand Down
57 changes: 49 additions & 8 deletions src/apprt/gtk/App.zig
Original file line number Diff line number Diff line change
Expand Up @@ -382,8 +382,8 @@ fn updateConfigErrors(self: *App) !void {

fn syncActionAccelerators(self: *App) !void {
try self.syncActionAccelerator("app.quit", .{ .quit = {} });
try self.syncActionAccelerator("app.open_config", .{ .open_config = {} });
try self.syncActionAccelerator("app.reload_config", .{ .reload_config = {} });
try self.syncActionAccelerator("app.open-config", .{ .open_config = {} });
try self.syncActionAccelerator("app.reload-config", .{ .reload_config = {} });
try self.syncActionAccelerator("win.toggle_inspector", .{ .inspector = .toggle });
try self.syncActionAccelerator("win.close", .{ .close_surface = {} });
try self.syncActionAccelerator("win.new_window", .{ .new_window = {} });
Expand Down Expand Up @@ -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 should be called only once on startup.
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 = .{
.{ "quit", &gtkActionQuit },
.{ "open_config", &gtkActionOpenConfig },
.{ "reload_config", &gtkActionReloadConfig },
.{ "quit", &gtkActionQuit, null },
.{ "open-config", &gtkActionOpenConfig, null },
.{ "reload-config", &gtkActionReloadConfig, null },
.{ "present-surface", &gtkActionPresentSurface, c.G_VARIANT_TYPE("t") },
};

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);
_ = c.g_signal_connect_data(
action,
Expand Down Expand Up @@ -871,8 +912,8 @@ fn initMenu(self: *App) void {
defer c.g_object_unref(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, "Open Configuration", "app.open_config");
c.g_menu_append(section, "Reload Configuration", "app.reload_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, "About Ghostty", "win.about");
}

Expand Down
28 changes: 23 additions & 5 deletions src/apprt/gtk/Surface.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1144,19 +1144,26 @@ pub fn showDesktopNotification(
else => title,
};

const notif = c.g_notification_new(t.ptr);
defer c.g_object_unref(notif);
c.g_notification_set_body(notif, body.ptr);
const notification = c.g_notification_new(t.ptr);
defer c.g_object_unref(notification);
c.g_notification_set_body(notification, body.ptr);

const icon = c.g_themed_icon_new("com.mitchellh.ghostty");
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);

// We set the notification ID to the body content. If the content is the
// 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 {
Expand Down Expand Up @@ -1967,3 +1974,14 @@ fn translateMods(state: c.GdkModifierType) input.Mods {
if (state & c.GDK_LOCK_MASK != 0) mods.caps_lock = true;
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();
}
4 changes: 4 additions & 0 deletions src/apprt/surface.zig
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ pub const Message = union(enum) {
/// Report the color scheme
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 {
csi_21_t,

Expand Down

0 comments on commit 779b721

Please sign in to comment.