apprt/gtk-ng: initialize window protocol

This commit is contained in:
Mitchell Hashimoto
2025-08-01 14:39:43 -07:00
parent 469001b7f6
commit c7eee9ee7a
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 adw_version = @import("../adw_version.zig");
const gresource = @import("../build/gresource.zig");
const winprotopkg = @import("../winproto.zig");
const Common = @import("../class.zig").Common;
const Config = @import("config.zig").Config;
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 name = "tabs-autohide";
const impl = gobject.ext.defineProperty(
@@ -205,12 +226,19 @@ pub const Window = extern 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.
tab_bindings: *gobject.BindingGroup,
/// The configuration that this surface is using.
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
/// initialized any single surface yet. We need this because we
/// 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();
}
// 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.
if (comptime build_config.is_debug) {
self.as(gtk.Widget).addCssClass("devel");
@@ -535,6 +567,11 @@ pub const Window = extern struct {
//---------------------------------------------------------------
// 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.
/// This does not ref the value.
fn getActiveSurface(self: *Self) ?*Surface {
@@ -542,6 +579,12 @@ pub const Window = extern struct {
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.
fn getSelectedTab(self: *Self) ?*Tab {
const priv = self.private();
@@ -731,6 +774,7 @@ pub const Window = extern struct {
fn finalize(self: *Self) callconv(.C) void {
const priv = self.private();
priv.tab_bindings.unref();
priv.winproto.deinit(Application.default().allocator());
gobject.Object.virtual_methods.finalize.call(
Class.parent,
@@ -741,6 +785,26 @@ pub const Window = extern struct {
//---------------------------------------------------------------
// 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 {
self.performBindingAction(.new_tab);
}
@@ -1376,6 +1440,7 @@ pub const Window = extern struct {
class.bindTemplateChildPrivate("toast_overlay", .{});
// Template Callbacks
class.bindTemplateCallback("realize", &windowRealize);
class.bindTemplateCallback("new_tab", &btnNewTab);
class.bindTemplateCallback("overview_create_tab", &tabOverviewCreateTab);
class.bindTemplateCallback("overview_notify_open", &tabOverviewOpen);

View File

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

View File

@@ -7,9 +7,7 @@ const gdk = @import("gdk");
const Config = @import("../../config.zig").Config;
const input = @import("../../input.zig");
const key = @import("key.zig");
// TODO: As we get to these APIs the compiler should tell us
const ApprtWindow = void;
const ApprtWindow = @import("class/window.zig").Window;
pub const noop = @import("winproto/noop.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 input = @import("../../../input.zig");
const ApprtWindow = void; // TODO: fix
const ApprtWindow = @import("../class/window.zig").Window;
const log = std.log.scoped(.winproto_noop);

View File

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

View File

@@ -20,7 +20,7 @@ pub const c = @cImport({
const input = @import("../../../input.zig");
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);
@@ -170,8 +170,7 @@ pub const App = struct {
pub const Window = struct {
app: *App,
config: *const ApprtWindow.DerivedConfig,
gtk_window: *adw.ApplicationWindow,
apprt_window: *ApprtWindow,
x11_surface: *gdk_x11.X11Surface,
blur_region: Region = .{},
@@ -183,9 +182,8 @@ pub const Window = struct {
) !Window {
_ = alloc;
const surface = apprt_window.window.as(
gtk.Native,
).getSurface() orelse return error.NotX11Surface;
const surface = apprt_window.as(gtk.Native).getSurface() orelse
return error.NotX11Surface;
const x11_surface = gobject.ext.cast(
gdk_x11.X11Surface,
@@ -194,8 +192,7 @@ pub const Window = struct {
return .{
.app = app,
.config = &apprt_window.config,
.gtk_window = apprt_window.window,
.apprt_window = apprt_window,
.x11_surface = x11_surface,
};
}
@@ -221,10 +218,10 @@ pub const Window = struct {
var x: 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.
const scale: f64 = @floatFromInt(self.gtk_window.as(gtk.Widget).getScaleFactor());
const scale: f64 = @floatFromInt(self.apprt_window.as(gtk.Widget).getScaleFactor());
x *= scale;
y *= scale;
@@ -257,10 +254,10 @@ pub const Window = struct {
// and I think it's not really noticeable enough to justify the effort.
// (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.
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.height = gtk_widget.getHeight() * scale;