apprt/gtk-ng: initialize window protocol

pull/8123/head
Mitchell Hashimoto 2025-08-01 14:39:43 -07:00
parent 469001b7f6
commit c7eee9ee7a
No known key found for this signature in database
GPG Key ID: 523D5DC389D273BC
6 changed files with 83 additions and 22 deletions

View File

@ -15,6 +15,7 @@ const ext = @import("../ext.zig");
const gtk_version = @import("../gtk_version.zig"); const gtk_version = @import("../gtk_version.zig");
const adw_version = @import("../adw_version.zig"); const adw_version = @import("../adw_version.zig");
const gresource = @import("../build/gresource.zig"); const gresource = @import("../build/gresource.zig");
const winprotopkg = @import("../winproto.zig");
const Common = @import("../class.zig").Common; const Common = @import("../class.zig").Common;
const Config = @import("config.zig").Config; const Config = @import("config.zig").Config;
const Application = @import("application.zig").Application; const Application = @import("application.zig").Application;
@ -131,6 +132,26 @@ pub const Window = extern struct {
); );
}; };
pub const @"quick-terminal" = struct {
pub const name = "quick-terminal";
const impl = gobject.ext.defineProperty(
name,
Self,
bool,
.{
.nick = "Quick Terminal",
.blurb = "Whether this window behaves like a quick terminal.",
.default = true,
.accessor = gobject.ext.privateFieldAccessor(
Self,
Private,
&Private.offset,
"quick_terminal",
),
},
);
};
pub const @"tabs-autohide" = struct { pub const @"tabs-autohide" = struct {
pub const name = "tabs-autohide"; pub const name = "tabs-autohide";
const impl = gobject.ext.defineProperty( const impl = gobject.ext.defineProperty(
@ -205,12 +226,19 @@ pub const Window = extern struct {
}; };
const Private = struct { const Private = struct {
/// Whether this window is a quick terminal. If it is then it
/// behaves slightly differently under certain scenarios.
quick_terminal: bool = false,
/// Binding group for our active tab. /// Binding group for our active tab.
tab_bindings: *gobject.BindingGroup, tab_bindings: *gobject.BindingGroup,
/// The configuration that this surface is using. /// The configuration that this surface is using.
config: ?*Config = null, config: ?*Config = null,
/// State and logic for windowing protocol for a window.
winproto: winprotopkg.Window,
/// Kind of hacky to have this but this lets us know if we've /// Kind of hacky to have this but this lets us know if we've
/// initialized any single surface yet. We need this because we /// initialized any single surface yet. We need this because we
/// gate default size on this so that we don't resize the window /// gate default size on this so that we don't resize the window
@ -253,6 +281,10 @@ pub const Window = extern struct {
priv.config = app.getConfig(); priv.config = app.getConfig();
} }
// We initialize our windowing protocol to none because we can't
// actually initialize this until we get realized.
priv.winproto = .none;
// Add our dev CSS class if we're in debug mode. // Add our dev CSS class if we're in debug mode.
if (comptime build_config.is_debug) { if (comptime build_config.is_debug) {
self.as(gtk.Widget).addCssClass("devel"); self.as(gtk.Widget).addCssClass("devel");
@ -535,6 +567,11 @@ pub const Window = extern struct {
//--------------------------------------------------------------- //---------------------------------------------------------------
// Properties // Properties
/// Whether this terminal is a quick terminal or not.
pub fn isQuickTerminal(self: *Self) bool {
return self.private().quick_terminal;
}
/// Get the currently active surface. See the "active-surface" property. /// Get the currently active surface. See the "active-surface" property.
/// This does not ref the value. /// This does not ref the value.
fn getActiveSurface(self: *Self) ?*Surface { fn getActiveSurface(self: *Self) ?*Surface {
@ -542,6 +579,12 @@ pub const Window = extern struct {
return tab.getActiveSurface(); return tab.getActiveSurface();
} }
/// Returns the configuration for this window. The reference count
/// is not increased.
pub fn getConfig(self: *Self) ?*Config {
return self.private().config;
}
/// Get the currently selected tab as a Tab object. /// Get the currently selected tab as a Tab object.
fn getSelectedTab(self: *Self) ?*Tab { fn getSelectedTab(self: *Self) ?*Tab {
const priv = self.private(); const priv = self.private();
@ -731,6 +774,7 @@ pub const Window = extern struct {
fn finalize(self: *Self) callconv(.C) void { fn finalize(self: *Self) callconv(.C) void {
const priv = self.private(); const priv = self.private();
priv.tab_bindings.unref(); priv.tab_bindings.unref();
priv.winproto.deinit(Application.default().allocator());
gobject.Object.virtual_methods.finalize.call( gobject.Object.virtual_methods.finalize.call(
Class.parent, Class.parent,
@ -741,6 +785,26 @@ pub const Window = extern struct {
//--------------------------------------------------------------- //---------------------------------------------------------------
// Signal handlers // Signal handlers
fn windowRealize(_: *gtk.Widget, self: *Window) callconv(.c) void {
const app = Application.default();
// Initialize our window protocol logic
if (winprotopkg.Window.init(
app.allocator(),
app.winproto(),
self,
)) |wp| {
self.private().winproto = wp;
} else |err| {
log.warn("failed to initialize window protocol error={}", .{err});
return;
}
// When we are realized we always setup our appearance since this
// calls some winproto functions.
self.syncAppearance();
}
fn btnNewTab(_: *adw.SplitButton, self: *Self) callconv(.c) void { fn btnNewTab(_: *adw.SplitButton, self: *Self) callconv(.c) void {
self.performBindingAction(.new_tab); self.performBindingAction(.new_tab);
} }
@ -1376,6 +1440,7 @@ pub const Window = extern struct {
class.bindTemplateChildPrivate("toast_overlay", .{}); class.bindTemplateChildPrivate("toast_overlay", .{});
// Template Callbacks // Template Callbacks
class.bindTemplateCallback("realize", &windowRealize);
class.bindTemplateCallback("new_tab", &btnNewTab); class.bindTemplateCallback("new_tab", &btnNewTab);
class.bindTemplateCallback("overview_create_tab", &tabOverviewCreateTab); class.bindTemplateCallback("overview_create_tab", &tabOverviewCreateTab);
class.bindTemplateCallback("overview_notify_open", &tabOverviewOpen); class.bindTemplateCallback("overview_notify_open", &tabOverviewOpen);

View File

@ -7,6 +7,7 @@ template $GhosttyWindow: Adw.ApplicationWindow {
] ]
close-request => $close_request(); close-request => $close_request();
realize => $realize();
notify::config => $notify_config(); notify::config => $notify_config();
notify::fullscreened => $notify_fullscreened(); notify::fullscreened => $notify_fullscreened();
notify::maximized => $notify_maximized(); notify::maximized => $notify_maximized();

View File

@ -7,9 +7,7 @@ const gdk = @import("gdk");
const Config = @import("../../config.zig").Config; const Config = @import("../../config.zig").Config;
const input = @import("../../input.zig"); const input = @import("../../input.zig");
const key = @import("key.zig"); const key = @import("key.zig");
const ApprtWindow = @import("class/window.zig").Window;
// TODO: As we get to these APIs the compiler should tell us
const ApprtWindow = void;
pub const noop = @import("winproto/noop.zig"); pub const noop = @import("winproto/noop.zig");
pub const x11 = @import("winproto/x11.zig"); pub const x11 = @import("winproto/x11.zig");

View File

@ -5,7 +5,7 @@ const gdk = @import("gdk");
const Config = @import("../../../config.zig").Config; const Config = @import("../../../config.zig").Config;
const input = @import("../../../input.zig"); const input = @import("../../../input.zig");
const ApprtWindow = void; // TODO: fix const ApprtWindow = @import("../class/window.zig").Window;
const log = std.log.scoped(.winproto_noop); const log = std.log.scoped(.winproto_noop);

View File

@ -12,7 +12,7 @@ const wayland = @import("wayland");
const Config = @import("../../../config.zig").Config; const Config = @import("../../../config.zig").Config;
const input = @import("../../../input.zig"); const input = @import("../../../input.zig");
const ApprtWindow = void; // TODO: fix const ApprtWindow = @import("../class/window.zig").Window;
const wl = wayland.client.wl; const wl = wayland.client.wl;
const org = wayland.client.org; const org = wayland.client.org;
@ -257,7 +257,7 @@ pub const Window = struct {
) !Window { ) !Window {
_ = alloc; _ = alloc;
const gtk_native = apprt_window.window.as(gtk.Native); const gtk_native = apprt_window.as(gtk.Native);
const gdk_surface = gtk_native.getSurface() orelse return error.NotWaylandSurface; const gdk_surface = gtk_native.getSurface() orelse return error.NotWaylandSurface;
// This should never fail, because if we're being called at this point // This should never fail, because if we're being called at this point
@ -470,14 +470,14 @@ pub const Window = struct {
monitor: *gdk.Monitor, monitor: *gdk.Monitor,
apprt_window: *ApprtWindow, apprt_window: *ApprtWindow,
) callconv(.c) void { ) callconv(.c) void {
const window = apprt_window.window.as(gtk.Window); const window = apprt_window.as(gtk.Window);
const config = &apprt_window.config; const config = if (apprt_window.getConfig()) |v| v.get() else return;
var monitor_size: gdk.Rectangle = undefined; var monitor_size: gdk.Rectangle = undefined;
monitor.getGeometry(&monitor_size); monitor.getGeometry(&monitor_size);
const dims = config.quick_terminal_size.calculate( const dims = config.@"quick-terminal-size".calculate(
config.quick_terminal_position, config.@"quick-terminal-position",
.{ .{
.width = @intCast(monitor_size.f_width), .width = @intCast(monitor_size.f_width),
.height = @intCast(monitor_size.f_height), .height = @intCast(monitor_size.f_height),

View File

@ -20,7 +20,7 @@ pub const c = @cImport({
const input = @import("../../../input.zig"); const input = @import("../../../input.zig");
const Config = @import("../../../config.zig").Config; const Config = @import("../../../config.zig").Config;
const ApprtWindow = void; // TODO: fix const ApprtWindow = @import("../class/window.zig").Window;
const log = std.log.scoped(.gtk_x11); const log = std.log.scoped(.gtk_x11);
@ -170,8 +170,7 @@ pub const App = struct {
pub const Window = struct { pub const Window = struct {
app: *App, app: *App,
config: *const ApprtWindow.DerivedConfig, apprt_window: *ApprtWindow,
gtk_window: *adw.ApplicationWindow,
x11_surface: *gdk_x11.X11Surface, x11_surface: *gdk_x11.X11Surface,
blur_region: Region = .{}, blur_region: Region = .{},
@ -183,9 +182,8 @@ pub const Window = struct {
) !Window { ) !Window {
_ = alloc; _ = alloc;
const surface = apprt_window.window.as( const surface = apprt_window.as(gtk.Native).getSurface() orelse
gtk.Native, return error.NotX11Surface;
).getSurface() orelse return error.NotX11Surface;
const x11_surface = gobject.ext.cast( const x11_surface = gobject.ext.cast(
gdk_x11.X11Surface, gdk_x11.X11Surface,
@ -194,8 +192,7 @@ pub const Window = struct {
return .{ return .{
.app = app, .app = app,
.config = &apprt_window.config, .apprt_window = apprt_window,
.gtk_window = apprt_window.window,
.x11_surface = x11_surface, .x11_surface = x11_surface,
}; };
} }
@ -221,10 +218,10 @@ pub const Window = struct {
var x: f64 = 0; var x: f64 = 0;
var y: f64 = 0; var y: f64 = 0;
self.gtk_window.as(gtk.Native).getSurfaceTransform(&x, &y); self.apprt_window.as(gtk.Native).getSurfaceTransform(&x, &y);
// Transform surface coordinates to device coordinates. // Transform surface coordinates to device coordinates.
const scale: f64 = @floatFromInt(self.gtk_window.as(gtk.Widget).getScaleFactor()); const scale: f64 = @floatFromInt(self.apprt_window.as(gtk.Widget).getScaleFactor());
x *= scale; x *= scale;
y *= scale; y *= scale;
@ -257,10 +254,10 @@ pub const Window = struct {
// and I think it's not really noticeable enough to justify the effort. // and I think it's not really noticeable enough to justify the effort.
// (Wayland also has this visual artifact anyway...) // (Wayland also has this visual artifact anyway...)
const gtk_widget = self.gtk_window.as(gtk.Widget); const gtk_widget = self.apprt_window.as(gtk.Widget);
// Transform surface coordinates to device coordinates. // Transform surface coordinates to device coordinates.
const scale = self.gtk_window.as(gtk.Widget).getScaleFactor(); const scale = self.apprt_window.as(gtk.Widget).getScaleFactor();
self.blur_region.width = gtk_widget.getWidth() * scale; self.blur_region.width = gtk_widget.getWidth() * scale;
self.blur_region.height = gtk_widget.getHeight() * scale; self.blur_region.height = gtk_widget.getHeight() * scale;