only initialize font discovery mechanism once, cache on App

Fontconfig in particular appears unsafe to initialize multiple times.

Font discovery is a singleton object in an application and only ever
accessed from the main thread so we can work around this by only
initializing and caching the font discovery mechanism exactly once on
the app singleton.
This commit is contained in:
Mitchell Hashimoto
2023-08-13 08:01:33 -07:00
parent a8426a90dd
commit 619d2ade3e
6 changed files with 52 additions and 18 deletions

View File

@ -45,6 +45,11 @@ quit: bool,
/// from source. This is null if we can't detect it.
resources_dir: ?[]const u8 = null,
/// Font discovery mechanism. This is only safe to use from the main thread.
/// This is lazily initialized on the first call to fontDiscover so do
/// not access this directly.
font_discover: ?font.Discover = null,
/// Initialize the main app instance. This creates the main window, sets
/// up the renderer state, compiles the shaders, etc. This is the primary
/// "startup" logic.
@ -80,6 +85,8 @@ pub fn destroy(self: *App) void {
self.surfaces.deinit(self.alloc);
if (self.resources_dir) |dir| self.alloc.free(dir);
if (self.font_discover) |*v| v.deinit();
self.alloc.destroy(self);
}
@ -151,6 +158,20 @@ pub fn focusedSurface(self: *const App) ?*Surface {
return surface;
}
/// Initialize once and return the font discovery mechanism. This remains
/// initialized throughout the lifetime of the application because some
/// font discovery mechanisms (i.e. fontconfig) are unsafe to reinit.
pub fn fontDiscover(self: *App) !?font.Discover {
// If we're built without a font discovery mechanism, return null
if (comptime font.Discover == void) return null;
// If we initialized, use it
if (self.font_discover) |v| return v;
self.font_discover = font.Discover.init();
return self.font_discover.?;
}
/// Drain the mailbox.
fn drainMailbox(self: *App, rt_app: *apprt.App) !void {
while (self.mailbox.pop()) |message| {

View File

@ -177,8 +177,8 @@ pub fn init(
self: *Surface,
alloc: Allocator,
config: *const configpkg.Config,
app: *App,
app_mailbox: App.Mailbox,
app_resources_dir: ?[]const u8,
rt_surface: *apprt.runtime.Surface,
) !void {
// Initialize our renderer with our initialized surface.
@ -217,8 +217,11 @@ pub fn init(
errdefer group.deinit();
// Search for fonts
if (font.Discover != void) {
var disco = font.Discover.init();
if (font.Discover != void) discover: {
const disco = try app.fontDiscover() orelse {
log.warn("font discovery not available, cannot search for fonts", .{});
break :discover;
};
group.discover = disco;
if (config.@"font-family") |family| {
@ -320,8 +323,7 @@ pub fn init(
// If we're on Mac, then we try to use the Apple Emoji font for Emoji.
if (builtin.os.tag == .macos and font.Discover != void) {
var disco = font.Discover.init();
defer disco.deinit();
if (try app.fontDiscover()) |disco| {
var disco_it = try disco.discover(.{
.family = "Apple Color Emoji",
.size = font_size.points,
@ -332,6 +334,7 @@ pub fn init(
try group.addFace(alloc, .regular, face);
}
}
}
break :group group;
});
@ -402,7 +405,7 @@ pub fn init(
.screen_size = screen_size,
.full_config = config,
.config = try termio.Impl.DerivedConfig.init(alloc, config),
.resources_dir = app_resources_dir,
.resources_dir = app.resources_dir,
.renderer_state = &self.renderer_state,
.renderer_wakeup = render_thread.wakeup,
.renderer_mailbox = render_thread.mailbox,

View File

@ -196,8 +196,8 @@ pub const Surface = struct {
try self.core_surface.init(
app.core_app.alloc,
&config,
app.core_app,
.{ .rt_app = app, .mailbox = &app.core_app.mailbox },
app.core_app.resources_dir,
self,
);
errdefer self.core_surface.deinit();

View File

@ -374,8 +374,8 @@ pub const Surface = struct {
try self.core_surface.init(
app.app.alloc,
&config,
app.app,
.{ .rt_app = app, .mailbox = &app.app.mailbox },
app.app.resources_dir,
self,
);
errdefer self.core_surface.deinit();

View File

@ -789,8 +789,8 @@ pub const Surface = struct {
try self.core_surface.init(
self.app.core_app.alloc,
&config,
self.app.core_app,
.{ .rt_app = self.app, .mailbox = &self.app.core_app.mailbox },
self.app.core_app.resources_dir,
self,
);
errdefer self.core_surface.deinit();
@ -1203,6 +1203,14 @@ pub const Surface = struct {
break :key input.Key.fromASCII(self.im_buf[0]) orelse physical_key;
} else .invalid;
// log.debug("key pressed key={} physical_key={} composing={} text_len={} mods={}", .{
// key,
// physical_key,
// self.im_composing,
// self.im_len,
// mods,
// });
// If both keys are invalid then we won't call the key callback. But
// if either one is valid, we want to give it a chance.
if (key != .invalid or physical_key != .invalid) {
@ -1342,6 +1350,8 @@ pub const Surface = struct {
if (str.len <= self.im_buf.len) {
@memcpy(self.im_buf[0..str.len], str);
self.im_len = @intCast(str.len);
// log.debug("input commit: {x}", .{self.im_buf[0]});
} else {
log.warn("not enough buffer space for input method commit", .{});
}

View File

@ -168,7 +168,7 @@ pub const Fontconfig = struct {
/// Discover fonts from a descriptor. This returns an iterator that can
/// be used to build up the deferred fonts.
pub fn discover(self: *Fontconfig, desc: Descriptor) !DiscoverIterator {
pub fn discover(self: *const Fontconfig, desc: Descriptor) !DiscoverIterator {
// Build our pattern that we'll search for
const pat = desc.toFcPattern();
errdefer pat.destroy();