mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-09-05 19:08:17 +00:00
apprt/gtk-ng: present surface
This commit is contained in:
@@ -521,6 +521,8 @@ pub const Application = extern struct {
|
||||
|
||||
.pwd => Action.pwd(target, value),
|
||||
|
||||
.present_terminal => return Action.presentTerminal(target),
|
||||
|
||||
.progress_report => return Action.progressReport(target, value),
|
||||
|
||||
.quit => self.quit(),
|
||||
@@ -550,7 +552,6 @@ pub const Application = extern struct {
|
||||
.goto_split,
|
||||
.inspector,
|
||||
.desktop_notification,
|
||||
.present_terminal,
|
||||
.initial_size,
|
||||
.size_limit,
|
||||
.toggle_split_zoom,
|
||||
@@ -828,6 +829,7 @@ pub const Application = extern struct {
|
||||
.{ "new-window", actionNewWindow, null },
|
||||
.{ "new-window-command", actionNewWindow, as_variant_type },
|
||||
.{ "open-config", actionOpenConfig, null },
|
||||
.{ "present-surface", actionPresentSurface, t_variant_type },
|
||||
.{ "quit", actionQuit, null },
|
||||
.{ "reload-config", actionReloadConfig, null },
|
||||
};
|
||||
@@ -1156,6 +1158,50 @@ pub const Application = extern struct {
|
||||
_ = self.core().mailbox.push(.open_config, .forever);
|
||||
}
|
||||
|
||||
fn actionPresentSurface(
|
||||
_: *gio.SimpleAction,
|
||||
parameter_: ?*glib.Variant,
|
||||
self: *Self,
|
||||
) callconv(.c) void {
|
||||
const parameter = parameter_ orelse return;
|
||||
|
||||
const t = glib.ext.VariantType.newFor(u64);
|
||||
defer glib.VariantType.free(t);
|
||||
|
||||
// Make sure that we've receiived a u64 from the system.
|
||||
if (glib.Variant.isOfType(parameter, t) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert that u64 to pointer to a core surface. A value of zero
|
||||
// means that there was no target surface for the notification so
|
||||
// we don't focus any surface.
|
||||
//
|
||||
// This is admittedly SUPER SUS and we should instead do what we
|
||||
// do on macOS which is generate a UUID per surface and then pass
|
||||
// that around. But, we do validate the pointer below so at worst
|
||||
// this may result in focusing the wrong surface if the pointer was
|
||||
// reused for a surface.
|
||||
const ptr_int = parameter.getUint64();
|
||||
if (ptr_int == 0) return;
|
||||
const surface: *CoreSurface = @ptrFromInt(ptr_int);
|
||||
|
||||
// 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().mailbox.push(
|
||||
.{
|
||||
.surface_message = .{
|
||||
.surface = surface,
|
||||
.message = .present_surface,
|
||||
},
|
||||
},
|
||||
.forever,
|
||||
);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Boilerplate/Noise
|
||||
|
||||
@@ -1445,6 +1491,18 @@ const Action = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn presentTerminal(
|
||||
target: apprt.Target,
|
||||
) bool {
|
||||
return switch (target) {
|
||||
.app => false,
|
||||
.surface => |v| surface: {
|
||||
v.rt_surface.surface.present();
|
||||
break :surface true;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn progressReport(
|
||||
target: apprt.Target,
|
||||
value: terminal.osc.Command.ProgressReport,
|
||||
|
@@ -285,6 +285,19 @@ pub const Surface = extern struct {
|
||||
);
|
||||
};
|
||||
|
||||
/// Emitted when the focus wants to be brought to the top and
|
||||
/// focused.
|
||||
pub const @"present-request" = struct {
|
||||
pub const name = "present-request";
|
||||
pub const connect = impl.connect;
|
||||
const impl = gobject.ext.defineSignal(
|
||||
name,
|
||||
Self,
|
||||
&.{},
|
||||
void,
|
||||
);
|
||||
};
|
||||
|
||||
/// Emitted when this surface requests its container to toggle its
|
||||
/// fullscreen state.
|
||||
pub const @"toggle-fullscreen" = struct {
|
||||
@@ -578,6 +591,17 @@ pub const Surface = extern struct {
|
||||
return @intFromBool(glib.SOURCE_REMOVE);
|
||||
}
|
||||
|
||||
/// Request that this terminal come to the front and become focused.
|
||||
/// It is up to the embedding widget to react to this.
|
||||
pub fn present(self: *Self) void {
|
||||
signals.@"present-request".impl.emit(
|
||||
self,
|
||||
null,
|
||||
.{},
|
||||
null,
|
||||
);
|
||||
}
|
||||
|
||||
/// Key press event (press or release).
|
||||
///
|
||||
/// At a high level, we want to construct an `input.KeyEvent` and
|
||||
@@ -2173,6 +2197,7 @@ pub const Surface = extern struct {
|
||||
signals.bell.impl.register(.{});
|
||||
signals.@"clipboard-read".impl.register(.{});
|
||||
signals.@"clipboard-write".impl.register(.{});
|
||||
signals.@"present-request".impl.register(.{});
|
||||
signals.@"toggle-fullscreen".impl.register(.{});
|
||||
signals.@"toggle-maximize".impl.register(.{});
|
||||
|
||||
|
@@ -955,6 +955,13 @@ pub const Window = extern struct {
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
_ = Surface.signals.@"present-request".connect(
|
||||
surface,
|
||||
*Self,
|
||||
surfacePresentRequest,
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
_ = Surface.signals.@"clipboard-write".connect(
|
||||
surface,
|
||||
*Self,
|
||||
@@ -1093,6 +1100,50 @@ pub const Window = extern struct {
|
||||
}
|
||||
}
|
||||
|
||||
fn surfacePresentRequest(
|
||||
surface: *Surface,
|
||||
self: *Self,
|
||||
) callconv(.c) void {
|
||||
// Verify that this surface is actually in this window.
|
||||
{
|
||||
const surface_window = ext.getAncestor(
|
||||
Self,
|
||||
surface.as(gtk.Widget),
|
||||
) orelse {
|
||||
log.warn(
|
||||
"present request called for non-existent surface",
|
||||
.{},
|
||||
);
|
||||
return;
|
||||
};
|
||||
if (surface_window != self) {
|
||||
log.warn(
|
||||
"present request called for surface in different window",
|
||||
.{},
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the tab for this surface.
|
||||
const tab = ext.getAncestor(
|
||||
Tab,
|
||||
surface.as(gtk.Widget),
|
||||
) orelse {
|
||||
log.warn("present request surface not found", .{});
|
||||
return;
|
||||
};
|
||||
|
||||
// Get the page that contains this tab
|
||||
const priv = self.private();
|
||||
const tab_view = priv.tab_view;
|
||||
const page = tab_view.getPage(tab.as(gtk.Widget));
|
||||
tab_view.setSelectedPage(page);
|
||||
|
||||
// Grab focus
|
||||
surface.grabFocus();
|
||||
}
|
||||
|
||||
fn surfaceToggleFullscreen(
|
||||
surface: *Surface,
|
||||
self: *Self,
|
||||
|
Reference in New Issue
Block a user