From 4f3c4037aa35914af9633cbf7d051c9d9f85b65d Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Sat, 22 Feb 2025 12:54:08 -0600 Subject: [PATCH] gtk: get 'Change Title' working with older distros --- src/apprt/gtk/Builder.zig | 2 +- src/apprt/gtk/Surface.zig | 15 ++++--- src/apprt/gtk/blueprint_compiler.zig | 57 ++++++++++++++++++++++++ src/apprt/gtk/gresource.zig | 20 ++++++--- src/apprt/gtk/ui/prompt-title-dialog.blp | 4 +- src/build/SharedDeps.zig | 24 +++++++--- src/build/docker/debian/Dockerfile | 2 + 7 files changed, 103 insertions(+), 21 deletions(-) create mode 100644 src/apprt/gtk/blueprint_compiler.zig diff --git a/src/apprt/gtk/Builder.zig b/src/apprt/gtk/Builder.zig index f9b0c226a..473abc0f7 100644 --- a/src/apprt/gtk/Builder.zig +++ b/src/apprt/gtk/Builder.zig @@ -25,7 +25,7 @@ pub fn init(comptime name: []const u8, comptime kind: enum { blp, ui }) Builder // GResource. const gresource = @import("gresource.zig"); for (gresource.blueprint_files) |blueprint_file| { - if (std.mem.eql(u8, blueprint_file, name)) break; + if (std.mem.eql(u8, blueprint_file.name, name)) break; } else @compileError("missing blueprint file '" ++ name ++ "' in gresource.zig"); }, .ui => { diff --git a/src/apprt/gtk/Surface.zig b/src/apprt/gtk/Surface.zig index 00799da6f..9836e7fbf 100644 --- a/src/apprt/gtk/Surface.zig +++ b/src/apprt/gtk/Surface.zig @@ -31,6 +31,7 @@ const inspector = @import("inspector.zig"); const gtk_key = @import("key.zig"); const c = @import("c.zig").c; const Builder = @import("Builder.zig"); +const adwaita = @import("adwaita.zig"); const log = std.log.scoped(.gtk_surface); @@ -1020,16 +1021,17 @@ fn resolveTitle(self: *Surface, title: [:0]const u8) [:0]const u8 { } pub fn promptTitle(self: *Surface) !void { + if (!adwaita.versionAtLeast(1, 5, 0)) return; const window = self.container.window() orelse return; var builder = Builder.init("prompt-title-dialog", .blp); defer builder.deinit(); - const entry: *gtk.Entry = @ptrCast(builder.getObject("title_entry")); + const entry = gobject.ext.cast(gtk.Entry, builder.getObject("title_entry").?).?; entry.getBuffer().setText(self.getTitle() orelse "", -1); - const dialog: *adw.AlertDialog = @ptrCast(builder.getObject("prompt_title_dialog")); - dialog.choose(@ptrCast(window.window), null, >kPromptTitleResponse, self); + const dialog = gobject.ext.cast(adw.AlertDialog, builder.getObject("prompt_title_dialog").?).?; + dialog.choose(@ptrCast(window.window), null, gtkPromptTitleResponse, self); } /// Set the current working directory of the surface. @@ -2320,12 +2322,13 @@ fn g_value_holds(value_: ?*c.GValue, g_type: c.GType) bool { } fn gtkPromptTitleResponse(source_object: ?*gobject.Object, result: *gio.AsyncResult, ud: ?*anyopaque) callconv(.C) void { - const dialog: *adw.AlertDialog = @ptrCast(source_object.?); - const self = userdataSelf(ud.?); + if (!adwaita.versionAtLeast(1, 5, 0)) return; + const dialog = gobject.ext.cast(adw.AlertDialog, source_object.?).?; + const self = userdataSelf(ud orelse return); const response = dialog.chooseFinish(result); if (std.mem.orderZ(u8, "ok", response) == .eq) { - const title_entry: *gtk.Entry = gobject.ext.cast(gtk.Entry, dialog.getExtraChild().?).?; + const title_entry = gobject.ext.cast(gtk.Entry, dialog.getExtraChild().?).?; const title = std.mem.span(title_entry.getBuffer().getText()); // if the new title is empty and the user has set the title previously, restore the terminal provided title diff --git a/src/apprt/gtk/blueprint_compiler.zig b/src/apprt/gtk/blueprint_compiler.zig new file mode 100644 index 000000000..f1d42c43d --- /dev/null +++ b/src/apprt/gtk/blueprint_compiler.zig @@ -0,0 +1,57 @@ +const std = @import("std"); + +pub const c = @cImport({ + @cInclude("adwaita.h"); +}); + +pub fn main() !void { + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + const alloc = gpa.allocator(); + + var it = try std.process.argsWithAllocator(alloc); + defer it.deinit(); + + _ = it.next(); + + const major = try std.fmt.parseUnsigned(u8, it.next() orelse return error.NoMajorVersion, 10); + const minor = try std.fmt.parseUnsigned(u8, it.next() orelse return error.NoMinorVersion, 10); + const micro = try std.fmt.parseUnsigned(u8, it.next() orelse return error.NoMicroVersion, 10); + const output = it.next() orelse return error.NoOutput; + const input = it.next() orelse return error.NoInput; + + if (c.ADW_MAJOR_VERSION < major or + (c.ADW_MAJOR_VERSION == major and c.ADW_MINOR_VERSION < minor) or + (c.ADW_MAJOR_VERSION == major and c.ADW_MINOR_VERSION == minor and c.ADW_MICRO_VERSION < micro)) + { + // If the Adwaita version is too old, generate an "empty" file. + const file = try std.fs.createFileAbsolute(output, .{ + .truncate = true, + }); + try file.writeAll( + \\ + \\ + ); + defer file.close(); + + return; + } + + var compiler = std.process.Child.init( + &.{ + "blueprint-compiler", + "compile", + "--output", + output, + input, + }, + alloc, + ); + + const term = try compiler.spawnAndWait(); + switch (term) { + .Exited => |rc| { + if (rc != 0) std.posix.exit(1); + }, + else => std.posix.exit(1), + } +} diff --git a/src/apprt/gtk/gresource.zig b/src/apprt/gtk/gresource.zig index 82452bb53..83978c337 100644 --- a/src/apprt/gtk/gresource.zig +++ b/src/apprt/gtk/gresource.zig @@ -57,7 +57,17 @@ pub const ui_files = [_][]const u8{ "menu-window-titlebar_menu", "menu-surface-context_menu", }; -pub const blueprint_files = [_][]const u8{"prompt-title-dialog"}; + +pub const VersionedBlueprint = struct { + major: u16, + minor: u16, + micro: u16, + name: []const u8, +}; + +pub const blueprint_files = [_]VersionedBlueprint{ + .{ .major = 1, .minor = 5, .micro = 0, .name = "prompt-title-dialog" }, +}; pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; @@ -72,9 +82,9 @@ pub fn main() !void { var it = try std.process.argsWithAllocator(alloc); defer it.deinit(); - while (it.next()) |filename| { - if (std.mem.eql(u8, std.fs.path.extension(filename), ".ui")) { - try extra_ui_files.append(try alloc.dupe(u8, filename)); + while (it.next()) |argument| { + if (std.mem.eql(u8, std.fs.path.extension(argument), ".ui")) { + try extra_ui_files.append(try alloc.dupe(u8, argument)); } } @@ -144,7 +154,7 @@ pub const dependencies = deps: { index += 1; } for (blueprint_files) |blueprint_file| { - deps[index] = std.fmt.comptimePrint("src/apprt/gtk/ui/{s}.blp", .{blueprint_file}); + deps[index] = std.fmt.comptimePrint("src/apprt/gtk/ui/{s}.blp", .{blueprint_file.name}); index += 1; } break :deps deps; diff --git a/src/apprt/gtk/ui/prompt-title-dialog.blp b/src/apprt/gtk/ui/prompt-title-dialog.blp index 7cd5a2657..ffe38c980 100644 --- a/src/apprt/gtk/ui/prompt-title-dialog.blp +++ b/src/apprt/gtk/ui/prompt-title-dialog.blp @@ -6,8 +6,8 @@ Adw.AlertDialog prompt_title_dialog { body: _("Leave blank to restore the default title."); responses [ - cancel: _("Cancel"), - ok: _("OK") suggested + cancel: _("Cancel") suggested, + ok: _("OK") destructive ] focus-widget: title_entry; diff --git a/src/build/SharedDeps.zig b/src/build/SharedDeps.zig index a90fc330a..65b2b47da 100644 --- a/src/build/SharedDeps.zig +++ b/src/build/SharedDeps.zig @@ -443,6 +443,7 @@ pub fn add( .{ "glib", "glib2" }, .{ "gtk", "gtk4" }, .{ "gdk", "gdk4" }, + .{ "adw", "adw1" }, }; inline for (gobject_imports) |import| { const name, const module = import; @@ -451,7 +452,6 @@ pub fn add( step.linkSystemLibrary2("gtk4", dynamic_link_opts); step.linkSystemLibrary2("libadwaita-1", dynamic_link_opts); - step.root_module.addImport("adw", gobject.module("adw1")); if (self.config.x11) { step.linkSystemLibrary2("X11", dynamic_link_opts); @@ -500,14 +500,24 @@ pub fn add( const generate = b.addRunArtifact(generate_gresource_xml); + const gtk_blueprint_compiler = b.addExecutable(.{ + .name = "gtk_blueprint_compiler", + .root_source_file = b.path("src/apprt/gtk/blueprint_compiler.zig"), + .target = b.host, + }); + gtk_blueprint_compiler.linkSystemLibrary2("gtk4", dynamic_link_opts); + gtk_blueprint_compiler.linkSystemLibrary2("libadwaita-1", dynamic_link_opts); + gtk_blueprint_compiler.linkLibC(); + for (gresource.blueprint_files) |blueprint_file| { - const blueprint_compiler = b.addSystemCommand(&.{ - "blueprint-compiler", - "compile", - "--output", + const blueprint_compiler = b.addRunArtifact(gtk_blueprint_compiler); + blueprint_compiler.addArgs(&.{ + b.fmt("{d}", .{blueprint_file.major}), + b.fmt("{d}", .{blueprint_file.minor}), + b.fmt("{d}", .{blueprint_file.micro}), }); - const ui_file = blueprint_compiler.addOutputFileArg(b.fmt("{s}.ui", .{blueprint_file})); - blueprint_compiler.addFileArg(b.path(b.fmt("src/apprt/gtk/ui/{s}.blp", .{blueprint_file}))); + const ui_file = blueprint_compiler.addOutputFileArg(b.fmt("{s}.ui", .{blueprint_file.name})); + blueprint_compiler.addFileArg(b.path(b.fmt("src/apprt/gtk/ui/{s}.blp", .{blueprint_file.name}))); generate.addFileArg(ui_file); } diff --git a/src/build/docker/debian/Dockerfile b/src/build/docker/debian/Dockerfile index 307fb7521..7f60ddf1d 100644 --- a/src/build/docker/debian/Dockerfile +++ b/src/build/docker/debian/Dockerfile @@ -5,9 +5,11 @@ FROM docker.io/library/debian:${DISTRO_VERSION} RUN DEBIAN_FRONTEND="noninteractive" apt-get -qq update && \ apt-get -qq -y --no-install-recommends install \ # Build Tools + blueprint-compiler \ build-essential \ libbz2-dev \ libonig-dev \ + libxml2-utils \ lintian \ lsb-release \ libxml2-utils \