mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 16:56:09 +03:00
apprt refactor in progress, launches glfw no window
This commit is contained in:
87
src/App.zig
87
src/App.zig
@ -30,9 +30,6 @@ pub const Mailbox = BlockingQueue(Message, 64);
|
|||||||
/// General purpose allocator
|
/// General purpose allocator
|
||||||
alloc: Allocator,
|
alloc: Allocator,
|
||||||
|
|
||||||
/// The runtime for this app.
|
|
||||||
runtime: apprt.runtime.App,
|
|
||||||
|
|
||||||
/// The list of windows that are currently open
|
/// The list of windows that are currently open
|
||||||
windows: WindowList,
|
windows: WindowList,
|
||||||
|
|
||||||
@ -46,35 +43,16 @@ mailbox: *Mailbox,
|
|||||||
/// Set to true once we're quitting. This never goes false again.
|
/// Set to true once we're quitting. This never goes false again.
|
||||||
quit: bool,
|
quit: bool,
|
||||||
|
|
||||||
/// Mac settings
|
/// App will call this when tick should be called.
|
||||||
darwin: if (Darwin.enabled) Darwin else void,
|
wakeup_cb: ?*const fn () void = null,
|
||||||
|
|
||||||
/// Mac-specific settings. This is only enabled when the target is
|
|
||||||
/// Mac and the artifact is a standalone exe. We don't target libs because
|
|
||||||
/// the embedded API doesn't do windowing.
|
|
||||||
pub const Darwin = struct {
|
|
||||||
pub const enabled = builtin.target.isDarwin() and build_config.artifact == .exe;
|
|
||||||
|
|
||||||
tabbing_id: *macos.foundation.String,
|
|
||||||
|
|
||||||
pub fn deinit(self: *Darwin) void {
|
|
||||||
self.tabbing_id.release();
|
|
||||||
self.* = undefined;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Initialize the main app instance. This creates the main window, sets
|
/// Initialize the main app instance. This creates the main window, sets
|
||||||
/// up the renderer state, compiles the shaders, etc. This is the primary
|
/// up the renderer state, compiles the shaders, etc. This is the primary
|
||||||
/// "startup" logic.
|
/// "startup" logic.
|
||||||
pub fn create(
|
pub fn create(
|
||||||
alloc: Allocator,
|
alloc: Allocator,
|
||||||
rt_opts: apprt.runtime.App.Options,
|
|
||||||
config: *const Config,
|
config: *const Config,
|
||||||
) !*App {
|
) !*App {
|
||||||
// Initialize app runtime
|
|
||||||
var app_backend = try apprt.runtime.App.init(rt_opts);
|
|
||||||
errdefer app_backend.terminate();
|
|
||||||
|
|
||||||
// The mailbox for messaging this thread
|
// The mailbox for messaging this thread
|
||||||
var mailbox = try Mailbox.create(alloc);
|
var mailbox = try Mailbox.create(alloc);
|
||||||
errdefer mailbox.destroy(alloc);
|
errdefer mailbox.destroy(alloc);
|
||||||
@ -86,36 +64,13 @@ pub fn create(
|
|||||||
errdefer alloc.destroy(app);
|
errdefer alloc.destroy(app);
|
||||||
app.* = .{
|
app.* = .{
|
||||||
.alloc = alloc,
|
.alloc = alloc,
|
||||||
.runtime = app_backend,
|
|
||||||
.windows = .{},
|
.windows = .{},
|
||||||
.config = config,
|
.config = config,
|
||||||
.mailbox = mailbox,
|
.mailbox = mailbox,
|
||||||
.quit = false,
|
.quit = false,
|
||||||
.darwin = if (Darwin.enabled) undefined else {},
|
|
||||||
};
|
};
|
||||||
errdefer app.windows.deinit(alloc);
|
errdefer app.windows.deinit(alloc);
|
||||||
|
|
||||||
// On Mac, we enable window tabbing. We only do this if we're building
|
|
||||||
// a standalone exe. In embedded mode the host app handles this for us.
|
|
||||||
if (Darwin.enabled) {
|
|
||||||
const NSWindow = objc.Class.getClass("NSWindow").?;
|
|
||||||
NSWindow.msgSend(void, objc.sel("setAllowsAutomaticWindowTabbing:"), .{true});
|
|
||||||
|
|
||||||
// Our tabbing ID allows all of our windows to group together
|
|
||||||
const tabbing_id = try macos.foundation.String.createWithBytes(
|
|
||||||
"dev.ghostty.window",
|
|
||||||
.utf8,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
errdefer tabbing_id.release();
|
|
||||||
|
|
||||||
// Setup our Mac settings
|
|
||||||
app.darwin = .{
|
|
||||||
.tabbing_id = tabbing_id,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
errdefer if (comptime builtin.target.isDarwin()) app.darwin.deinit();
|
|
||||||
|
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,35 +78,22 @@ pub fn destroy(self: *App) void {
|
|||||||
// Clean up all our windows
|
// Clean up all our windows
|
||||||
for (self.windows.items) |window| window.destroy();
|
for (self.windows.items) |window| window.destroy();
|
||||||
self.windows.deinit(self.alloc);
|
self.windows.deinit(self.alloc);
|
||||||
if (Darwin.enabled) self.darwin.deinit();
|
|
||||||
self.mailbox.destroy(self.alloc);
|
self.mailbox.destroy(self.alloc);
|
||||||
|
|
||||||
// Close our windowing runtime
|
|
||||||
self.runtime.terminate();
|
|
||||||
|
|
||||||
self.alloc.destroy(self);
|
self.alloc.destroy(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wake up the app event loop. This should be called after any messages
|
/// Request the app runtime to process app events via tick.
|
||||||
/// are sent to the mailbox.
|
|
||||||
pub fn wakeup(self: App) void {
|
pub fn wakeup(self: App) void {
|
||||||
self.runtime.wakeup() catch return;
|
if (self.wakeup_cb) |cb| cb();
|
||||||
}
|
|
||||||
|
|
||||||
/// Run the main event loop for the application. This blocks until the
|
|
||||||
/// application quits or every window is closed.
|
|
||||||
pub fn run(self: *App) !void {
|
|
||||||
while (!self.quit and self.windows.items.len > 0) {
|
|
||||||
try self.tick();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tick ticks the app loop. This will drain our mailbox and process those
|
/// Tick ticks the app loop. This will drain our mailbox and process those
|
||||||
/// events.
|
/// events. This should be called by the application runtime on every loop
|
||||||
pub fn tick(self: *App) !void {
|
/// tick.
|
||||||
// Block for any events.
|
///
|
||||||
try self.runtime.wait();
|
/// This returns whether the app should quit or not.
|
||||||
|
pub fn tick(self: *App, rt_app: *apprt.runtime.App) !bool {
|
||||||
// If any windows are closing, destroy them
|
// If any windows are closing, destroy them
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
while (i < self.windows.items.len) {
|
while (i < self.windows.items.len) {
|
||||||
@ -165,8 +107,11 @@ pub fn tick(self: *App) !void {
|
|||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// // Drain our mailbox only if we're not quitting.
|
// Drain our mailbox only if we're not quitting.
|
||||||
if (!self.quit) try self.drainMailbox();
|
if (!self.quit) try self.drainMailbox(rt_app);
|
||||||
|
|
||||||
|
// We quit if our quit flag is on or if we have closed all windows.
|
||||||
|
return self.quit or self.windows.items.len == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new window. This can be called only on the main thread. This
|
/// Create a new window. This can be called only on the main thread. This
|
||||||
@ -201,7 +146,9 @@ pub fn closeWindow(self: *App, window: *Window) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Drain the mailbox.
|
/// Drain the mailbox.
|
||||||
fn drainMailbox(self: *App) !void {
|
fn drainMailbox(self: *App, rt_app: *apprt.runtime.App) !void {
|
||||||
|
_ = rt_app;
|
||||||
|
|
||||||
while (self.mailbox.pop()) |message| {
|
while (self.mailbox.pop()) |message| {
|
||||||
log.debug("mailbox message={s}", .{@tagName(message)});
|
log.debug("mailbox message={s}", .{@tagName(message)});
|
||||||
switch (message) {
|
switch (message) {
|
||||||
|
@ -19,6 +19,22 @@ pub const browser = @import("apprt/browser.zig");
|
|||||||
pub const embedded = @import("apprt/embedded.zig");
|
pub const embedded = @import("apprt/embedded.zig");
|
||||||
pub const Window = @import("apprt/Window.zig");
|
pub const Window = @import("apprt/Window.zig");
|
||||||
|
|
||||||
|
/// The implementation to use for the app runtime. This is comptime chosen
|
||||||
|
/// so that every build has exactly one application runtime implementation.
|
||||||
|
/// Note: it is very rare to use Runtime directly; most usage will use
|
||||||
|
/// Window or something.
|
||||||
|
pub const runtime = switch (build_config.artifact) {
|
||||||
|
.exe => switch (build_config.app_runtime) {
|
||||||
|
.none => @compileError("exe with no runtime not allowed"),
|
||||||
|
.glfw => glfw,
|
||||||
|
.gtk => gtk,
|
||||||
|
},
|
||||||
|
.lib => embedded,
|
||||||
|
.wasm_module => browser,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const App = runtime.App;
|
||||||
|
|
||||||
/// Runtime is the runtime to use for Ghostty. All runtimes do not provide
|
/// Runtime is the runtime to use for Ghostty. All runtimes do not provide
|
||||||
/// equivalent feature sets. For example, GTK offers tabbing and more features
|
/// equivalent feature sets. For example, GTK offers tabbing and more features
|
||||||
/// that glfw does not provide. However, glfw may require many less
|
/// that glfw does not provide. However, glfw may require many less
|
||||||
@ -41,20 +57,6 @@ pub const Runtime = enum {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The implementation to use for the app runtime. This is comptime chosen
|
|
||||||
/// so that every build has exactly one application runtime implementation.
|
|
||||||
/// Note: it is very rare to use Runtime directly; most usage will use
|
|
||||||
/// Window or something.
|
|
||||||
pub const runtime = switch (build_config.artifact) {
|
|
||||||
.exe => switch (build_config.app_runtime) {
|
|
||||||
.none => @compileError("exe with no runtime not allowed"),
|
|
||||||
.glfw => glfw,
|
|
||||||
.gtk => gtk,
|
|
||||||
},
|
|
||||||
.lib => embedded,
|
|
||||||
.wasm_module => browser,
|
|
||||||
};
|
|
||||||
|
|
||||||
test {
|
test {
|
||||||
@import("std").testing.refAllDecls(@This());
|
@import("std").testing.refAllDecls(@This());
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,12 @@
|
|||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
|
const build_config = @import("../build_config.zig");
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const trace = @import("tracy").trace;
|
const trace = @import("tracy").trace;
|
||||||
const glfw = @import("glfw");
|
const glfw = @import("glfw");
|
||||||
|
const macos = @import("macos");
|
||||||
const objc = @import("objc");
|
const objc = @import("objc");
|
||||||
const input = @import("../input.zig");
|
const input = @import("../input.zig");
|
||||||
const internal_os = @import("../os/main.zig");
|
const internal_os = @import("../os/main.zig");
|
||||||
@ -26,11 +28,28 @@ const glfwNative = glfw.Native(.{
|
|||||||
const log = std.log.scoped(.glfw);
|
const log = std.log.scoped(.glfw);
|
||||||
|
|
||||||
pub const App = struct {
|
pub const App = struct {
|
||||||
|
app: *CoreApp,
|
||||||
|
|
||||||
|
/// Mac-specific state.
|
||||||
|
darwin: if (Darwin.enabled) Darwin else void,
|
||||||
|
|
||||||
pub const Options = struct {};
|
pub const Options = struct {};
|
||||||
|
|
||||||
pub fn init(_: Options) !App {
|
pub fn init(core_app: *CoreApp, _: Options) !App {
|
||||||
if (!glfw.init(.{})) return error.GlfwInitFailed;
|
if (!glfw.init(.{})) return error.GlfwInitFailed;
|
||||||
return .{};
|
glfw.setErrorCallback(glfwErrorCallback);
|
||||||
|
|
||||||
|
// Mac-specific state. For example, on Mac we enable window tabbing.
|
||||||
|
var darwin = if (Darwin.enabled) try Darwin.init() else {};
|
||||||
|
errdefer if (Darwin.enabled) darwin.deinit();
|
||||||
|
|
||||||
|
// Set our callback for being woken up
|
||||||
|
core_app.wakeup_cb = wakeup;
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.app = core_app,
|
||||||
|
.darwin = darwin,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn terminate(self: App) void {
|
pub fn terminate(self: App) void {
|
||||||
@ -38,17 +57,67 @@ pub const App = struct {
|
|||||||
glfw.terminate();
|
glfw.terminate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Run the event loop. This doesn't return until the app exits.
|
||||||
|
pub fn run(self: *App) !void {
|
||||||
|
while (true) {
|
||||||
|
// Wait for any events from the app event loop. wakeup will post
|
||||||
|
// an empty event so that this will return.
|
||||||
|
glfw.waitEvents();
|
||||||
|
|
||||||
|
// Tick the terminal app
|
||||||
|
const should_quit = try self.app.tick(self);
|
||||||
|
if (should_quit) return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Wakeup the event loop. This should be able to be called from any thread.
|
/// Wakeup the event loop. This should be able to be called from any thread.
|
||||||
pub fn wakeup(self: App) !void {
|
pub fn wakeup() void {
|
||||||
_ = self;
|
|
||||||
glfw.postEmptyEvent();
|
glfw.postEmptyEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wait for events in the event loop to process.
|
fn glfwErrorCallback(code: glfw.ErrorCode, desc: [:0]const u8) void {
|
||||||
pub fn wait(self: App) !void {
|
std.log.warn("glfw error={} message={s}", .{ code, desc });
|
||||||
_ = self;
|
|
||||||
glfw.waitEvents();
|
// Workaround for: https://github.com/ocornut/imgui/issues/5908
|
||||||
|
// If we get an invalid value with "scancode" in the message we assume
|
||||||
|
// it is from the glfw key callback that imgui sets and we clear the
|
||||||
|
// error so that our future code doesn't crash.
|
||||||
|
if (code == glfw.ErrorCode.InvalidValue and
|
||||||
|
std.mem.indexOf(u8, desc, "scancode") != null)
|
||||||
|
{
|
||||||
|
_ = glfw.getError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Mac-specific settings. This is only enabled when the target is
|
||||||
|
/// Mac and the artifact is a standalone exe. We don't target libs because
|
||||||
|
/// the embedded API doesn't do windowing.
|
||||||
|
const Darwin = struct {
|
||||||
|
const enabled = builtin.target.isDarwin() and build_config.artifact == .exe;
|
||||||
|
|
||||||
|
tabbing_id: *macos.foundation.String,
|
||||||
|
|
||||||
|
pub fn init() !Darwin {
|
||||||
|
const NSWindow = objc.Class.getClass("NSWindow").?;
|
||||||
|
NSWindow.msgSend(void, objc.sel("setAllowsAutomaticWindowTabbing:"), .{true});
|
||||||
|
|
||||||
|
// Our tabbing ID allows all of our windows to group together
|
||||||
|
const tabbing_id = try macos.foundation.String.createWithBytes(
|
||||||
|
"com.mitchellh.ghostty.window",
|
||||||
|
.utf8,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
errdefer tabbing_id.release();
|
||||||
|
|
||||||
|
// Setup our Mac settings
|
||||||
|
return .{ .tabbing_id = tabbing_id };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *Darwin) void {
|
||||||
|
self.tabbing_id.release();
|
||||||
|
self.* = undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Window = struct {
|
pub const Window = struct {
|
||||||
|
@ -14,6 +14,13 @@ pub const c = @cImport({
|
|||||||
|
|
||||||
const log = std.log.scoped(.gtk);
|
const log = std.log.scoped(.gtk);
|
||||||
|
|
||||||
|
/// App is the entrypoint for the application. This is called after all
|
||||||
|
/// of the runtime-agnostic initialization is complete and we're ready
|
||||||
|
/// to start.
|
||||||
|
///
|
||||||
|
/// There is only ever one App instance per process. This is because most
|
||||||
|
/// application frameworks also have this restriction so it simplifies
|
||||||
|
/// the assumptions.
|
||||||
pub const App = struct {
|
pub const App = struct {
|
||||||
pub const Options = struct {
|
pub const Options = struct {
|
||||||
/// GTK app ID
|
/// GTK app ID
|
||||||
@ -23,14 +30,15 @@ pub const App = struct {
|
|||||||
app: *c.GtkApplication,
|
app: *c.GtkApplication,
|
||||||
ctx: *c.GMainContext,
|
ctx: *c.GMainContext,
|
||||||
|
|
||||||
pub fn init(opts: Options) !App {
|
pub fn init(core_app: *CoreApp, opts: Options) !App {
|
||||||
|
_ = core_app;
|
||||||
|
|
||||||
|
// Create our GTK Application which encapsulates our process.
|
||||||
const app = @ptrCast(?*c.GtkApplication, c.gtk_application_new(
|
const app = @ptrCast(?*c.GtkApplication, c.gtk_application_new(
|
||||||
opts.id.ptr,
|
opts.id.ptr,
|
||||||
c.G_APPLICATION_DEFAULT_FLAGS,
|
c.G_APPLICATION_DEFAULT_FLAGS,
|
||||||
)) orelse return error.GtkInitFailed;
|
)) orelse return error.GtkInitFailed;
|
||||||
errdefer c.g_object_unref(app);
|
errdefer c.g_object_unref(app);
|
||||||
|
|
||||||
// Setup our callbacks
|
|
||||||
_ = c.g_signal_connect_data(
|
_ = c.g_signal_connect_data(
|
||||||
app,
|
app,
|
||||||
"activate",
|
"activate",
|
||||||
@ -69,6 +77,8 @@ pub const App = struct {
|
|||||||
return .{ .app = app, .ctx = ctx };
|
return .{ .app = app, .ctx = ctx };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Terminate the application. The application will not be restarted after
|
||||||
|
// this so all global state can be cleaned up.
|
||||||
pub fn terminate(self: App) void {
|
pub fn terminate(self: App) void {
|
||||||
c.g_settings_sync();
|
c.g_settings_sync();
|
||||||
while (c.g_main_context_iteration(self.ctx, 0) != 0) {}
|
while (c.g_main_context_iteration(self.ctx, 0) != 0) {}
|
||||||
@ -85,11 +95,56 @@ pub const App = struct {
|
|||||||
_ = c.g_main_context_iteration(self.ctx, 1);
|
_ = c.g_main_context_iteration(self.ctx, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn newWindow(self: App) !void {
|
||||||
|
const window = c.gtk_application_window_new(self.app);
|
||||||
|
c.gtk_window_set_title(@ptrCast(*c.GtkWindow, window), "Ghostty");
|
||||||
|
c.gtk_window_set_default_size(@ptrCast(*c.GtkWindow, window), 200, 200);
|
||||||
|
|
||||||
|
const surface = c.gtk_gl_area_new();
|
||||||
|
c.gtk_window_set_child(@ptrCast(*c.GtkWindow, window), surface);
|
||||||
|
_ = c.g_signal_connect_data(
|
||||||
|
surface,
|
||||||
|
"realize",
|
||||||
|
c.G_CALLBACK(&onSurfaceRealize),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
c.G_CONNECT_DEFAULT,
|
||||||
|
);
|
||||||
|
_ = c.g_signal_connect_data(
|
||||||
|
surface,
|
||||||
|
"render",
|
||||||
|
c.G_CALLBACK(&onSurfaceRender),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
c.G_CONNECT_DEFAULT,
|
||||||
|
);
|
||||||
|
|
||||||
|
c.gtk_widget_show(window);
|
||||||
|
}
|
||||||
|
|
||||||
fn activate(app: *c.GtkApplication, ud: ?*anyopaque) callconv(.C) void {
|
fn activate(app: *c.GtkApplication, ud: ?*anyopaque) callconv(.C) void {
|
||||||
_ = app;
|
_ = app;
|
||||||
_ = ud;
|
_ = ud;
|
||||||
|
|
||||||
|
// We purposely don't do anything on activation right now. We have
|
||||||
|
// this callback because if we don't then GTK emits a warning to
|
||||||
|
// stderr that we don't want. We emit a debug log just so that we know
|
||||||
|
// we reached this point.
|
||||||
log.debug("application activated", .{});
|
log.debug("application activated", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn onSurfaceRealize(area: *c.GtkGLArea, ud: ?*anyopaque) callconv(.C) void {
|
||||||
|
_ = area;
|
||||||
|
_ = ud;
|
||||||
|
log.debug("gl surface realized", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn onSurfaceRender(area: *c.GtkGLArea, ctx: *c.GdkGLContext, ud: ?*anyopaque) callconv(.C) void {
|
||||||
|
_ = area;
|
||||||
|
_ = ctx;
|
||||||
|
_ = ud;
|
||||||
|
log.debug("gl render", .{});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Window = struct {
|
pub const Window = struct {
|
||||||
|
41
src/main.zig
41
src/main.zig
@ -11,6 +11,7 @@ const fontconfig = @import("fontconfig");
|
|||||||
const harfbuzz = @import("harfbuzz");
|
const harfbuzz = @import("harfbuzz");
|
||||||
const renderer = @import("renderer.zig");
|
const renderer = @import("renderer.zig");
|
||||||
const xdg = @import("xdg.zig");
|
const xdg = @import("xdg.zig");
|
||||||
|
const apprt = @import("apprt.zig");
|
||||||
|
|
||||||
const App = @import("App.zig");
|
const App = @import("App.zig");
|
||||||
const cli_args = @import("cli_args.zig");
|
const cli_args = @import("cli_args.zig");
|
||||||
@ -89,18 +90,30 @@ pub fn main() !void {
|
|||||||
try config.finalize();
|
try config.finalize();
|
||||||
std.log.debug("config={}", .{config});
|
std.log.debug("config={}", .{config});
|
||||||
|
|
||||||
switch (build_config.app_runtime) {
|
if (true) {
|
||||||
.none => {},
|
// Create our app state
|
||||||
.glfw => {
|
var app = try App.create(alloc, &config);
|
||||||
// We want to log all our errors
|
defer app.destroy();
|
||||||
glfw.setErrorCallback(glfwErrorCallback);
|
|
||||||
},
|
// Create our runtime app
|
||||||
.gtk => {},
|
var app_runtime = try apprt.App.init(app, .{});
|
||||||
|
defer app_runtime.terminate();
|
||||||
|
|
||||||
|
// Create an initial window
|
||||||
|
|
||||||
|
// Run the GUI event loop
|
||||||
|
try app_runtime.run();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run our app with a single initial window to start.
|
// Run our app with a single initial window to start.
|
||||||
var app = try App.create(alloc, .{}, &config);
|
var app = try App.create(alloc, .{}, &config);
|
||||||
defer app.destroy();
|
defer app.destroy();
|
||||||
|
if (build_config.app_runtime == .gtk) {
|
||||||
|
try app.runtime.newWindow();
|
||||||
|
while (true) try app.runtime.wait();
|
||||||
|
return;
|
||||||
|
}
|
||||||
_ = try app.newWindow(.{});
|
_ = try app.newWindow(.{});
|
||||||
try app.run();
|
try app.run();
|
||||||
}
|
}
|
||||||
@ -158,20 +171,6 @@ pub const std_options = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fn glfwErrorCallback(code: glfw.ErrorCode, desc: [:0]const u8) void {
|
|
||||||
std.log.warn("glfw error={} message={s}", .{ code, desc });
|
|
||||||
|
|
||||||
// Workaround for: https://github.com/ocornut/imgui/issues/5908
|
|
||||||
// If we get an invalid value with "scancode" in the message we assume
|
|
||||||
// it is from the glfw key callback that imgui sets and we clear the
|
|
||||||
// error so that our future code doesn't crash.
|
|
||||||
if (code == glfw.ErrorCode.InvalidValue and
|
|
||||||
std.mem.indexOf(u8, desc, "scancode") != null)
|
|
||||||
{
|
|
||||||
_ = glfw.getError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This represents the global process state. There should only
|
/// This represents the global process state. There should only
|
||||||
/// be one of these at any given moment. This is extracted into a dedicated
|
/// be one of these at any given moment. This is extracted into a dedicated
|
||||||
/// struct because it is reused by main and the static C lib.
|
/// struct because it is reused by main and the static C lib.
|
||||||
|
Reference in New Issue
Block a user