gtk: better reporting for CSS parsing problems (#9129)
Log messages will include the problematic CSS, simplifying debugging.
Especially helpful since some of our CSS is generated at runtime so it
could be difficult to examine the CSS "source".
```
info(gtk_ghostty_application): loading gtk-custom-css path=/home/ghostty/dev/ghostty/x.css
warning(gtk_ghostty_application): css parsing failed at <data>:2:3-14: gtk-css-parser-error-quark 4 No property named "border-poop"
* {
border-poop: 0;
warning(gtk_ghostty_application): css parsing failed at <data>:1:3-3:1: gtk-css-parser-warning-quark 1 Unterminated block at end of document
* {
border-poop: 0;
```
vs:
```
info(gtk_ghostty_application): loading gtk-custom-css path=/home/ghostty/dev/ghostty/x.css
warning(glib): WARNING: Gtk: Theme parser error: <data>:2:3-14: No property named "border-poop"
warning(glib): WARNING: Gtk: Theme parser warning: <data>:1:3-3:1: Unterminated block at end of document
```
pull/9139/head
parent
854c8e6975
commit
c5ad7563f9
|
|
@ -394,6 +394,14 @@ pub const Application = extern struct {
|
|||
.{ .detail = "config" },
|
||||
);
|
||||
|
||||
_ = gtk.CssProvider.signals.parsing_error.connect(
|
||||
css_provider,
|
||||
*Self,
|
||||
signalCssParsingError,
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
|
||||
// Trigger initial config changes
|
||||
self.as(gobject.Object).notifyByPspec(properties.config.impl.param_spec);
|
||||
|
||||
|
|
@ -812,8 +820,8 @@ pub const Application = extern struct {
|
|||
|
||||
fn loadRuntimeCss(self: *Self) (Allocator.Error || std.Io.Writer.Error)!void {
|
||||
const alloc = self.allocator();
|
||||
|
||||
const config = self.private().config.get();
|
||||
const priv: *Private = self.private();
|
||||
const config = priv.config.get();
|
||||
|
||||
var buf: std.Io.Writer.Allocating = try .initCapacity(alloc, 2048);
|
||||
defer buf.deinit();
|
||||
|
|
@ -862,19 +870,15 @@ pub const Application = extern struct {
|
|||
, .{ .font_family = font_family });
|
||||
}
|
||||
|
||||
// ensure that we have a sentinel
|
||||
try writer.writeByte(0);
|
||||
const contents = buf.written();
|
||||
|
||||
const data_ = buf.written();
|
||||
const data = data_[0 .. data_.len - 1 :0];
|
||||
log.debug("runtime CSS is {d} bytes", .{contents.len});
|
||||
|
||||
log.debug("runtime CSS is {d} bytes", .{data.len + 1});
|
||||
const bytes = glib.Bytes.new(contents.ptr, contents.len);
|
||||
defer bytes.unref();
|
||||
|
||||
// Clears any previously loaded CSS from this provider
|
||||
loadCssProviderFromData(
|
||||
self.private().css_provider,
|
||||
data,
|
||||
);
|
||||
priv.css_provider.loadFromBytes(bytes);
|
||||
}
|
||||
|
||||
/// Load runtime CSS for older than GTK 4.16
|
||||
|
|
@ -1013,8 +1017,8 @@ pub const Application = extern struct {
|
|||
}
|
||||
}
|
||||
|
||||
fn loadCustomCss(self: *Self) !void {
|
||||
const priv = self.private();
|
||||
fn loadCustomCss(self: *Self) (std.fs.File.ReadError || Allocator.Error)!void {
|
||||
const priv: *Private = self.private();
|
||||
const alloc = self.allocator();
|
||||
const display = gdk.Display.getDefault() orelse {
|
||||
log.warn("unable to get display", .{});
|
||||
|
|
@ -1031,7 +1035,7 @@ pub const Application = extern struct {
|
|||
}
|
||||
priv.custom_css_providers.clearRetainingCapacity();
|
||||
|
||||
const config = priv.config.getMut();
|
||||
const config = priv.config.get();
|
||||
for (config.@"gtk-custom-css".value.items) |p| {
|
||||
const path, const optional = switch (p) {
|
||||
.optional => |path| .{ path, true },
|
||||
|
|
@ -1048,23 +1052,42 @@ pub const Application = extern struct {
|
|||
};
|
||||
defer file.close();
|
||||
|
||||
const css_file_size_limit = 5 * 1024 * 1024; // 5MB
|
||||
|
||||
log.info("loading gtk-custom-css path={s}", .{path});
|
||||
const contents = try file.readToEndAlloc(
|
||||
const contents = file.readToEndAlloc(
|
||||
alloc,
|
||||
5 * 1024 * 1024, // 5MB,
|
||||
);
|
||||
css_file_size_limit,
|
||||
) catch |err| switch (err) {
|
||||
error.FileTooBig => {
|
||||
log.warn("gtk-custom-css file {s} was larger than {Bi}", .{ path, css_file_size_limit });
|
||||
continue;
|
||||
},
|
||||
else => |e| return e,
|
||||
};
|
||||
defer alloc.free(contents);
|
||||
|
||||
const data = try alloc.dupeZ(u8, contents);
|
||||
defer alloc.free(data);
|
||||
const bytes = glib.Bytes.new(contents.ptr, contents.len);
|
||||
defer bytes.unref();
|
||||
|
||||
const css_provider = gtk.CssProvider.new();
|
||||
errdefer css_provider.unref();
|
||||
|
||||
_ = gtk.CssProvider.signals.parsing_error.connect(
|
||||
css_provider,
|
||||
*Self,
|
||||
signalCssParsingError,
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
|
||||
try priv.custom_css_providers.append(alloc, css_provider);
|
||||
|
||||
css_provider.loadFromBytes(bytes);
|
||||
|
||||
const provider = gtk.CssProvider.new();
|
||||
errdefer provider.unref();
|
||||
try priv.custom_css_providers.append(alloc, provider);
|
||||
loadCssProviderFromData(provider, data);
|
||||
gtk.StyleContext.addProviderForDisplay(
|
||||
display,
|
||||
provider.as(gtk.StyleProvider),
|
||||
css_provider.as(gtk.StyleProvider),
|
||||
gtk.STYLE_PROVIDER_PRIORITY_USER,
|
||||
);
|
||||
}
|
||||
|
|
@ -1180,6 +1203,37 @@ pub const Application = extern struct {
|
|||
};
|
||||
}
|
||||
|
||||
/// Log CSS parsing error
|
||||
fn signalCssParsingError(
|
||||
_: *gtk.CssProvider,
|
||||
css_section: *gtk.CssSection,
|
||||
err: *glib.Error,
|
||||
_: *Self,
|
||||
) callconv(.c) void {
|
||||
const location = css_section.toString();
|
||||
defer glib.free(location);
|
||||
if (comptime gtk_version.atLeast(4, 16, 0)) bytes: {
|
||||
const bytes = css_section.getBytes() orelse break :bytes;
|
||||
var len: usize = undefined;
|
||||
const ptr = bytes.getData(&len) orelse break :bytes;
|
||||
const data = ptr[0..len];
|
||||
log.warn("css parsing failed at {s}: {s} {d} {s}\n{s}", .{
|
||||
location,
|
||||
glib.quarkToString(err.f_domain),
|
||||
err.f_code,
|
||||
err.f_message orelse "«unknown»",
|
||||
data,
|
||||
});
|
||||
return;
|
||||
}
|
||||
log.warn("css parsing failed at {s}: {s} {d} {s}", .{
|
||||
location,
|
||||
glib.quarkToString(err.f_domain),
|
||||
err.f_code,
|
||||
err.f_message orelse "«unknown»",
|
||||
});
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Libghostty Callbacks
|
||||
|
||||
|
|
@ -2583,8 +2637,3 @@ fn findActiveWindow(data: ?*const anyopaque, _: ?*const anyopaque) callconv(.c)
|
|||
// Abusing integers to be enums and booleans is a terrible idea, C.
|
||||
return if (window.isActive() != 0) 0 else -1;
|
||||
}
|
||||
|
||||
fn loadCssProviderFromData(provider: *gtk.CssProvider, data: [:0]const u8) void {
|
||||
assert(gtk_version.runtimeAtLeast(4, 12, 0));
|
||||
provider.loadFromString(data);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue