mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-22 19:56:08 +03:00
Integrating new surface
This commit is contained in:
169
src/App.zig
169
src/App.zig
@ -9,7 +9,7 @@ const assert = std.debug.assert;
|
|||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const build_config = @import("build_config.zig");
|
const build_config = @import("build_config.zig");
|
||||||
const apprt = @import("apprt.zig");
|
const apprt = @import("apprt.zig");
|
||||||
const Window = @import("Window.zig");
|
const Surface = @import("Surface.zig");
|
||||||
const tracy = @import("tracy");
|
const tracy = @import("tracy");
|
||||||
const input = @import("input.zig");
|
const input = @import("input.zig");
|
||||||
const Config = @import("config.zig").Config;
|
const Config = @import("config.zig").Config;
|
||||||
@ -22,7 +22,8 @@ const DevMode = @import("DevMode.zig");
|
|||||||
|
|
||||||
const log = std.log.scoped(.app);
|
const log = std.log.scoped(.app);
|
||||||
|
|
||||||
const WindowList = std.ArrayListUnmanaged(*Window);
|
const SurfaceList = std.ArrayListUnmanaged(*apprt.Surface);
|
||||||
|
const SurfacePool = std.heap.MemoryPool(apprt.Surface);
|
||||||
|
|
||||||
/// The type used for sending messages to the app thread.
|
/// The type used for sending messages to the app thread.
|
||||||
pub const Mailbox = BlockingQueue(Message, 64);
|
pub const Mailbox = BlockingQueue(Message, 64);
|
||||||
@ -30,8 +31,14 @@ pub const Mailbox = BlockingQueue(Message, 64);
|
|||||||
/// General purpose allocator
|
/// General purpose allocator
|
||||||
alloc: Allocator,
|
alloc: Allocator,
|
||||||
|
|
||||||
/// The list of windows that are currently open
|
/// The list of surfaces that are currently active.
|
||||||
windows: WindowList,
|
surfaces: SurfaceList,
|
||||||
|
|
||||||
|
/// The memory pool to request surfaces. We use a memory pool because surfaces
|
||||||
|
/// typically require stable pointers due to runtime GUI callbacks. Centralizing
|
||||||
|
/// all the allocations in this pool makes it so that all our pools remain
|
||||||
|
/// close in memory.
|
||||||
|
surface_pool: SurfacePool,
|
||||||
|
|
||||||
// The configuration for the app.
|
// The configuration for the app.
|
||||||
config: *const Config,
|
config: *const Config,
|
||||||
@ -64,20 +71,23 @@ pub fn create(
|
|||||||
errdefer alloc.destroy(app);
|
errdefer alloc.destroy(app);
|
||||||
app.* = .{
|
app.* = .{
|
||||||
.alloc = alloc,
|
.alloc = alloc,
|
||||||
.windows = .{},
|
.surfaces = .{},
|
||||||
|
.surface_pool = try SurfacePool.initPreheated(alloc, 2),
|
||||||
.config = config,
|
.config = config,
|
||||||
.mailbox = mailbox,
|
.mailbox = mailbox,
|
||||||
.quit = false,
|
.quit = false,
|
||||||
};
|
};
|
||||||
errdefer app.windows.deinit(alloc);
|
errdefer app.surfaces.deinit(alloc);
|
||||||
|
errdefer app.surface_pool.deinit();
|
||||||
|
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn destroy(self: *App) void {
|
pub fn destroy(self: *App) void {
|
||||||
// Clean up all our windows
|
// Clean up all our surfaces
|
||||||
for (self.windows.items) |window| window.destroy();
|
for (self.surfaces.items) |surface| surface.deinit();
|
||||||
self.windows.deinit(self.alloc);
|
self.surfaces.deinit(self.alloc);
|
||||||
|
self.surface_pool.deinit();
|
||||||
self.mailbox.destroy(self.alloc);
|
self.mailbox.destroy(self.alloc);
|
||||||
|
|
||||||
self.alloc.destroy(self);
|
self.alloc.destroy(self);
|
||||||
@ -94,13 +104,14 @@ pub fn wakeup(self: App) void {
|
|||||||
///
|
///
|
||||||
/// This returns whether the app should quit or not.
|
/// This returns whether the app should quit or not.
|
||||||
pub fn tick(self: *App, rt_app: *apprt.runtime.App) !bool {
|
pub fn tick(self: *App, rt_app: *apprt.runtime.App) !bool {
|
||||||
// If any windows are closing, destroy them
|
// If any surfaces are closing, destroy them
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
while (i < self.windows.items.len) {
|
while (i < self.surfaces.items.len) {
|
||||||
const window = self.windows.items[i];
|
const surface = self.surfaces.items[i];
|
||||||
if (window.shouldClose()) {
|
if (surface.shouldClose()) {
|
||||||
window.destroy();
|
surface.deinit();
|
||||||
_ = self.windows.swapRemove(i);
|
_ = self.surfaces.swapRemove(i);
|
||||||
|
self.surface_pool.destroy(surface);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,52 +121,56 @@ pub fn tick(self: *App, rt_app: *apprt.runtime.App) !bool {
|
|||||||
// Drain our mailbox only if we're not quitting.
|
// Drain our mailbox only if we're not quitting.
|
||||||
if (!self.quit) try self.drainMailbox(rt_app);
|
if (!self.quit) try self.drainMailbox(rt_app);
|
||||||
|
|
||||||
// We quit if our quit flag is on or if we have closed all windows.
|
// We quit if our quit flag is on or if we have closed all surfaces.
|
||||||
return self.quit or self.windows.items.len == 0;
|
return self.quit or self.surfaces.items.len == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new window. This can be called only on the main thread. This
|
/// Add an initialized surface. This is really only for the runtime
|
||||||
/// can be called prior to ever running the app loop.
|
/// implementations to call and should NOT be called by general app users.
|
||||||
pub fn newWindow(self: *App, msg: Message.NewWindow) !*Window {
|
/// The surface must be from the pool.
|
||||||
var window = try Window.create(self.alloc, self, self.config, msg.runtime);
|
pub fn addSurface(self: *App, rt_surface: *apprt.Surface) !void {
|
||||||
errdefer window.destroy();
|
try self.surfaces.append(self.alloc, rt_surface);
|
||||||
|
}
|
||||||
|
|
||||||
try self.windows.append(self.alloc, window);
|
/// Delete the surface from the known surface list. This will NOT call the
|
||||||
errdefer _ = self.windows.pop();
|
/// destructor or free the memory.
|
||||||
|
pub fn deleteSurface(self: *App, rt_surface: *apprt.Surface) void {
|
||||||
// Set initial font size if given
|
var i: usize = 0;
|
||||||
if (msg.font_size) |size| window.setFontSize(size);
|
while (i < self.surfaces.items.len) {
|
||||||
|
if (self.surfaces.items[i] == rt_surface) {
|
||||||
return window;
|
_ = self.surfaces.swapRemove(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Close a window and free all resources associated with it. This can
|
/// Close a window and free all resources associated with it. This can
|
||||||
/// only be called from the main thread.
|
/// only be called from the main thread.
|
||||||
pub fn closeWindow(self: *App, window: *Window) void {
|
// pub fn closeWindow(self: *App, window: *Window) void {
|
||||||
var i: usize = 0;
|
// var i: usize = 0;
|
||||||
while (i < self.windows.items.len) {
|
// while (i < self.surfaces.items.len) {
|
||||||
const current = self.windows.items[i];
|
// const current = self.surfaces.items[i];
|
||||||
if (window == current) {
|
// if (window == current) {
|
||||||
window.destroy();
|
// window.destroy();
|
||||||
_ = self.windows.swapRemove(i);
|
// _ = self.surfaces.swapRemove(i);
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
i += 1;
|
// i += 1;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
/// Drain the mailbox.
|
/// Drain the mailbox.
|
||||||
fn drainMailbox(self: *App, rt_app: *apprt.runtime.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) {
|
||||||
.new_window => |msg| _ = try self.newWindow(msg),
|
.new_window => |msg| {
|
||||||
|
_ = msg; // TODO
|
||||||
|
try rt_app.newWindow();
|
||||||
|
},
|
||||||
.new_tab => |msg| try self.newTab(msg),
|
.new_tab => |msg| try self.newTab(msg),
|
||||||
.quit => try self.setQuit(),
|
.quit => try self.setQuit(),
|
||||||
.window_message => |msg| try self.windowMessage(msg.window, msg.message),
|
.surface_message => |msg| try self.surfaceMessage(msg.surface, msg.message),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -180,7 +195,7 @@ fn newTab(self: *App, msg: Message.NewWindow) !void {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// If the parent was closed prior to us handling the message, we do nothing.
|
// If the parent was closed prior to us handling the message, we do nothing.
|
||||||
if (!self.hasWindow(parent)) {
|
if (!self.hasSurface(parent)) {
|
||||||
log.warn("new_tab parent is gone, not launching a new tab", .{});
|
log.warn("new_tab parent is gone, not launching a new tab", .{});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -197,28 +212,28 @@ fn setQuit(self: *App) !void {
|
|||||||
if (self.quit) return;
|
if (self.quit) return;
|
||||||
self.quit = true;
|
self.quit = true;
|
||||||
|
|
||||||
// Mark that all our windows should close
|
// Mark that all our surfaces should close
|
||||||
for (self.windows.items) |window| {
|
for (self.surfaces.items) |surface| {
|
||||||
window.window.setShouldClose();
|
surface.setShouldClose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle a window message
|
/// Handle a window message
|
||||||
fn windowMessage(self: *App, win: *Window, msg: Window.Message) !void {
|
fn surfaceMessage(self: *App, surface: *Surface, msg: apprt.surface.Message) !void {
|
||||||
// We want to ensure our window is still active. Window messages
|
// We want to ensure our window is still active. Window messages
|
||||||
// are quite rare and we normally don't have many windows so we do
|
// are quite rare and we normally don't have many windows so we do
|
||||||
// a simple linear search here.
|
// a simple linear search here.
|
||||||
if (self.hasWindow(win)) {
|
if (self.hasSurface(surface)) {
|
||||||
try win.handleMessage(msg);
|
try surface.handleMessage(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Window was not found, it probably quit before we handled the message.
|
// Window was not found, it probably quit before we handled the message.
|
||||||
// Not a problem.
|
// Not a problem.
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hasWindow(self: *App, win: *Window) bool {
|
fn hasSurface(self: *App, surface: *Surface) bool {
|
||||||
for (self.windows.items) |window| {
|
for (self.surfaces.items) |v| {
|
||||||
if (window == win) return true;
|
if (&v.core_surface == surface) return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -237,18 +252,18 @@ pub const Message = union(enum) {
|
|||||||
/// Quit
|
/// Quit
|
||||||
quit: void,
|
quit: void,
|
||||||
|
|
||||||
/// A message for a specific window
|
/// A message for a specific surface.
|
||||||
window_message: struct {
|
surface_message: struct {
|
||||||
window: *Window,
|
surface: *Surface,
|
||||||
message: Window.Message,
|
message: apprt.surface.Message,
|
||||||
},
|
},
|
||||||
|
|
||||||
const NewWindow = struct {
|
const NewWindow = struct {
|
||||||
/// Runtime-specific window options.
|
/// Runtime-specific window options.
|
||||||
runtime: apprt.runtime.Window.Options = .{},
|
runtime: apprt.runtime.Surface.Options = .{},
|
||||||
|
|
||||||
/// The parent window, only used for new tabs.
|
/// The parent surface, only used for new tabs.
|
||||||
parent: ?*Window = null,
|
parent: ?*Surface = null,
|
||||||
|
|
||||||
/// The font size to create the window with or null to default to
|
/// The font size to create the window with or null to default to
|
||||||
/// the configuration amount.
|
/// the configuration amount.
|
||||||
@ -332,7 +347,7 @@ pub const CAPI = struct {
|
|||||||
export fn ghostty_surface_new(
|
export fn ghostty_surface_new(
|
||||||
app: *App,
|
app: *App,
|
||||||
opts: *const apprt.runtime.Window.Options,
|
opts: *const apprt.runtime.Window.Options,
|
||||||
) ?*Window {
|
) ?*Surface {
|
||||||
return surface_new_(app, opts) catch |err| {
|
return surface_new_(app, opts) catch |err| {
|
||||||
log.err("error initializing surface err={}", .{err});
|
log.err("error initializing surface err={}", .{err});
|
||||||
return null;
|
return null;
|
||||||
@ -342,46 +357,46 @@ pub const CAPI = struct {
|
|||||||
fn surface_new_(
|
fn surface_new_(
|
||||||
app: *App,
|
app: *App,
|
||||||
opts: *const apprt.runtime.Window.Options,
|
opts: *const apprt.runtime.Window.Options,
|
||||||
) !*Window {
|
) !*Surface {
|
||||||
const w = try app.newWindow(.{
|
const w = try app.newWindow(.{
|
||||||
.runtime = opts.*,
|
.runtime = opts.*,
|
||||||
});
|
});
|
||||||
return w;
|
return w;
|
||||||
}
|
}
|
||||||
|
|
||||||
export fn ghostty_surface_free(ptr: ?*Window) void {
|
export fn ghostty_surface_free(ptr: ?*Surface) void {
|
||||||
if (ptr) |v| v.app.closeWindow(v);
|
if (ptr) |v| v.app.closeWindow(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the app associated with a surface.
|
/// Returns the app associated with a surface.
|
||||||
export fn ghostty_surface_app(win: *Window) *App {
|
export fn ghostty_surface_app(win: *Surface) *App {
|
||||||
return win.app;
|
return win.app;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tell the surface that it needs to schedule a render
|
/// Tell the surface that it needs to schedule a render
|
||||||
export fn ghostty_surface_refresh(win: *Window) void {
|
export fn ghostty_surface_refresh(win: *Surface) void {
|
||||||
win.window.refresh();
|
win.window.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the size of a surface. This will trigger resize notifications
|
/// Update the size of a surface. This will trigger resize notifications
|
||||||
/// to the pty and the renderer.
|
/// to the pty and the renderer.
|
||||||
export fn ghostty_surface_set_size(win: *Window, w: u32, h: u32) void {
|
export fn ghostty_surface_set_size(win: *Surface, w: u32, h: u32) void {
|
||||||
win.window.updateSize(w, h);
|
win.window.updateSize(w, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the content scale of the surface.
|
/// Update the content scale of the surface.
|
||||||
export fn ghostty_surface_set_content_scale(win: *Window, x: f64, y: f64) void {
|
export fn ghostty_surface_set_content_scale(win: *Surface, x: f64, y: f64) void {
|
||||||
win.window.updateContentScale(x, y);
|
win.window.updateContentScale(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the focused state of a surface.
|
/// Update the focused state of a surface.
|
||||||
export fn ghostty_surface_set_focus(win: *Window, focused: bool) void {
|
export fn ghostty_surface_set_focus(win: *Surface, focused: bool) void {
|
||||||
win.window.focusCallback(focused);
|
win.window.focusCallback(focused);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tell the surface that it needs to schedule a render
|
/// Tell the surface that it needs to schedule a render
|
||||||
export fn ghostty_surface_key(
|
export fn ghostty_surface_key(
|
||||||
win: *Window,
|
win: *Surface,
|
||||||
action: input.Action,
|
action: input.Action,
|
||||||
key: input.Key,
|
key: input.Key,
|
||||||
mods: c_int,
|
mods: c_int,
|
||||||
@ -394,13 +409,13 @@ pub const CAPI = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Tell the surface that it needs to schedule a render
|
/// Tell the surface that it needs to schedule a render
|
||||||
export fn ghostty_surface_char(win: *Window, codepoint: u32) void {
|
export fn ghostty_surface_char(win: *Surface, codepoint: u32) void {
|
||||||
win.window.charCallback(codepoint);
|
win.window.charCallback(codepoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tell the surface that it needs to schedule a render
|
/// Tell the surface that it needs to schedule a render
|
||||||
export fn ghostty_surface_mouse_button(
|
export fn ghostty_surface_mouse_button(
|
||||||
win: *Window,
|
win: *Surface,
|
||||||
action: input.MouseButtonState,
|
action: input.MouseButtonState,
|
||||||
button: input.MouseButton,
|
button: input.MouseButton,
|
||||||
mods: c_int,
|
mods: c_int,
|
||||||
@ -413,15 +428,15 @@ pub const CAPI = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Update the mouse position within the view.
|
/// Update the mouse position within the view.
|
||||||
export fn ghostty_surface_mouse_pos(win: *Window, x: f64, y: f64) void {
|
export fn ghostty_surface_mouse_pos(win: *Surface, x: f64, y: f64) void {
|
||||||
win.window.cursorPosCallback(x, y);
|
win.window.cursorPosCallback(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
export fn ghostty_surface_mouse_scroll(win: *Window, x: f64, y: f64) void {
|
export fn ghostty_surface_mouse_scroll(win: *Surface, x: f64, y: f64) void {
|
||||||
win.window.scrollCallback(x, y);
|
win.window.scrollCallback(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
export fn ghostty_surface_ime_point(win: *Window, x: *f64, y: *f64) void {
|
export fn ghostty_surface_ime_point(win: *Surface, x: *f64, y: *f64) void {
|
||||||
const pos = win.imePoint();
|
const pos = win.imePoint();
|
||||||
x.* = pos.x;
|
x.* = pos.x;
|
||||||
y.* = pos.y;
|
y.* = pos.y;
|
||||||
|
@ -10,7 +10,7 @@ const Allocator = std.mem.Allocator;
|
|||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
|
|
||||||
const font = @import("font/main.zig");
|
const font = @import("font/main.zig");
|
||||||
const Window = @import("Window.zig");
|
const Surface = @import("Surface.zig");
|
||||||
const renderer = @import("renderer.zig");
|
const renderer = @import("renderer.zig");
|
||||||
const Config = @import("config.zig").Config;
|
const Config = @import("config.zig").Config;
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ visible: bool = false,
|
|||||||
config: ?*const Config = null,
|
config: ?*const Config = null,
|
||||||
|
|
||||||
/// The window we're tracking.
|
/// The window we're tracking.
|
||||||
window: ?*Window = null,
|
window: ?*Surface = null,
|
||||||
|
|
||||||
/// Update the state associated with the dev mode. This should generally
|
/// Update the state associated with the dev mode. This should generally
|
||||||
/// only be called paired with a render since it otherwise wastes CPU
|
/// only be called paired with a render since it otherwise wastes CPU
|
||||||
|
1635
src/Surface.zig
Normal file
1635
src/Surface.zig
Normal file
File diff suppressed because it is too large
Load Diff
@ -17,7 +17,7 @@ pub const glfw = @import("apprt/glfw.zig");
|
|||||||
pub const gtk = @import("apprt/gtk.zig");
|
pub const gtk = @import("apprt/gtk.zig");
|
||||||
pub const browser = @import("apprt/browser.zig");
|
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 surface = @import("apprt/Surface.zig");
|
||||||
|
|
||||||
/// The implementation to use for the app runtime. This is comptime chosen
|
/// The implementation to use for the app runtime. This is comptime chosen
|
||||||
/// so that every build has exactly one application runtime implementation.
|
/// so that every build has exactly one application runtime implementation.
|
||||||
@ -34,6 +34,7 @@ pub const runtime = switch (build_config.artifact) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub const App = runtime.App;
|
pub const App = runtime.App;
|
||||||
|
pub const Surface = runtime.Surface;
|
||||||
|
|
||||||
/// 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
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
const App = @import("../App.zig");
|
const App = @import("../App.zig");
|
||||||
const Window = @import("../Window.zig");
|
const Surface = @import("../Surface.zig");
|
||||||
const renderer = @import("../renderer.zig");
|
const renderer = @import("../renderer.zig");
|
||||||
const termio = @import("../termio.zig");
|
const termio = @import("../termio.zig");
|
||||||
|
|
||||||
@ -27,17 +27,17 @@ pub const Message = union(enum) {
|
|||||||
|
|
||||||
/// A window mailbox.
|
/// A window mailbox.
|
||||||
pub const Mailbox = struct {
|
pub const Mailbox = struct {
|
||||||
window: *Window,
|
window: *Surface,
|
||||||
app: *App.Mailbox,
|
app: *App.Mailbox,
|
||||||
|
|
||||||
/// Send a message to the window.
|
/// Send a message to the window.
|
||||||
pub fn push(self: Mailbox, msg: Message, timeout: App.Mailbox.Timeout) App.Mailbox.Size {
|
pub fn push(self: Mailbox, msg: Message, timeout: App.Mailbox.Timeout) App.Mailbox.Size {
|
||||||
// Window message sending is actually implemented on the app
|
// Surface message sending is actually implemented on the app
|
||||||
// thread, so we have to rewrap the message with our window
|
// thread, so we have to rewrap the message with our window
|
||||||
// pointer and send it to the app thread.
|
// pointer and send it to the app thread.
|
||||||
const result = self.app.push(.{
|
const result = self.app.push(.{
|
||||||
.window_message = .{
|
.surface_message = .{
|
||||||
.window = self.window,
|
.surface = self.window,
|
||||||
.message = msg,
|
.message = msg,
|
||||||
},
|
},
|
||||||
}, timeout);
|
}, timeout);
|
@ -18,7 +18,7 @@ const renderer = @import("../renderer.zig");
|
|||||||
const Renderer = renderer.Renderer;
|
const Renderer = renderer.Renderer;
|
||||||
const apprt = @import("../apprt.zig");
|
const apprt = @import("../apprt.zig");
|
||||||
const CoreApp = @import("../App.zig");
|
const CoreApp = @import("../App.zig");
|
||||||
const CoreWindow = @import("../Window.zig");
|
const CoreSurface = @import("../Surface.zig");
|
||||||
|
|
||||||
// Get native API access on certain platforms so we can do more customization.
|
// Get native API access on certain platforms so we can do more customization.
|
||||||
const glfwNative = glfw.Native(.{
|
const glfwNative = glfw.Native(.{
|
||||||
@ -75,6 +75,16 @@ pub const App = struct {
|
|||||||
glfw.postEmptyEvent();
|
glfw.postEmptyEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new window for the app.
|
||||||
|
pub fn newWindow(self: *App) !void {
|
||||||
|
// Grab a surface allocation because we're going to need it.
|
||||||
|
const surface = try self.app.surface_pool.create();
|
||||||
|
errdefer self.app.surface_pool.destroy(surface);
|
||||||
|
|
||||||
|
// Create the surface -- because windows are surfaces for glfw.
|
||||||
|
try surface.init(self);
|
||||||
|
}
|
||||||
|
|
||||||
fn glfwErrorCallback(code: glfw.ErrorCode, desc: [:0]const u8) void {
|
fn glfwErrorCallback(code: glfw.ErrorCode, desc: [:0]const u8) void {
|
||||||
std.log.warn("glfw error={} message={s}", .{ code, desc });
|
std.log.warn("glfw error={} message={s}", .{ code, desc });
|
||||||
|
|
||||||
@ -120,18 +130,32 @@ pub const App = struct {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Window = struct {
|
/// Surface represents the drawable surface for glfw. In glfw, a surface
|
||||||
|
/// is always a window because that is the only abstraction that glfw exposes.
|
||||||
|
///
|
||||||
|
/// This means that there is no way for the glfw runtime to support tabs,
|
||||||
|
/// splits, etc. without considerable effort. In fact, on Darwin, we do
|
||||||
|
/// support tabs because the minimal tabbing interface is a window abstraction,
|
||||||
|
/// but this is a bit of a hack. The native Swift runtime should be used instead
|
||||||
|
/// which uses real native tabbing.
|
||||||
|
///
|
||||||
|
/// Other runtimes a surface usually represents the equivalent of a "view"
|
||||||
|
/// or "widget" level granularity.
|
||||||
|
pub const Surface = struct {
|
||||||
/// The glfw window handle
|
/// The glfw window handle
|
||||||
window: glfw.Window,
|
window: glfw.Window,
|
||||||
|
|
||||||
/// The glfw mouse cursor handle.
|
/// The glfw mouse cursor handle.
|
||||||
cursor: glfw.Cursor,
|
cursor: glfw.Cursor,
|
||||||
|
|
||||||
|
/// A core surface
|
||||||
|
core_surface: CoreSurface,
|
||||||
|
|
||||||
pub const Options = struct {};
|
pub const Options = struct {};
|
||||||
|
|
||||||
pub fn init(app: *const CoreApp, core_win: *CoreWindow, opts: Options) !Window {
|
/// Initialize the surface into the given self pointer. This gives a
|
||||||
_ = opts;
|
/// stable pointer to the destination that can be used for callbacks.
|
||||||
|
pub fn init(self: *Surface, app: *App) !void {
|
||||||
// Create our window
|
// Create our window
|
||||||
const win = glfw.Window.create(
|
const win = glfw.Window.create(
|
||||||
640,
|
640,
|
||||||
@ -143,9 +167,9 @@ pub const Window = struct {
|
|||||||
) orelse return glfw.mustGetErrorCode();
|
) orelse return glfw.mustGetErrorCode();
|
||||||
errdefer win.destroy();
|
errdefer win.destroy();
|
||||||
|
|
||||||
if (builtin.mode == .Debug) {
|
|
||||||
// Get our physical DPI - debug only because we don't have a use for
|
// Get our physical DPI - debug only because we don't have a use for
|
||||||
// this but the logging of it may be useful
|
// this but the logging of it may be useful
|
||||||
|
if (builtin.mode == .Debug) {
|
||||||
const monitor = win.getMonitor() orelse monitor: {
|
const monitor = win.getMonitor() orelse monitor: {
|
||||||
log.warn("window had null monitor, getting primary monitor", .{});
|
log.warn("window had null monitor, getting primary monitor", .{});
|
||||||
break :monitor glfw.Monitor.getPrimary().?;
|
break :monitor glfw.Monitor.getPrimary().?;
|
||||||
@ -160,8 +184,8 @@ pub const Window = struct {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// On Mac, enable tabbing
|
// On Mac, enable window tabbing
|
||||||
if (comptime builtin.target.isDarwin()) {
|
if (App.Darwin.enabled) {
|
||||||
const NSWindowTabbingMode = enum(usize) { automatic = 0, preferred = 1, disallowed = 2 };
|
const NSWindowTabbingMode = enum(usize) { automatic = 0, preferred = 1, disallowed = 2 };
|
||||||
const nswindow = objc.Object.fromId(glfwNative.getCocoaWindow(win).?);
|
const nswindow = objc.Object.fromId(glfwNative.getCocoaWindow(win).?);
|
||||||
|
|
||||||
@ -184,7 +208,7 @@ pub const Window = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set our callbacks
|
// Set our callbacks
|
||||||
win.setUserPointer(core_win);
|
win.setUserPointer(&self.core_surface);
|
||||||
win.setSizeCallback(sizeCallback);
|
win.setSizeCallback(sizeCallback);
|
||||||
win.setCharCallback(charCallback);
|
win.setCharCallback(charCallback);
|
||||||
win.setKeyCallback(keyCallback);
|
win.setKeyCallback(keyCallback);
|
||||||
@ -195,13 +219,23 @@ pub const Window = struct {
|
|||||||
win.setMouseButtonCallback(mouseButtonCallback);
|
win.setMouseButtonCallback(mouseButtonCallback);
|
||||||
|
|
||||||
// Build our result
|
// Build our result
|
||||||
return Window{
|
self.* = .{
|
||||||
.window = win,
|
.window = win,
|
||||||
.cursor = cursor,
|
.cursor = cursor,
|
||||||
|
.core_surface = undefined,
|
||||||
};
|
};
|
||||||
|
errdefer self.* = undefined;
|
||||||
|
|
||||||
|
// Add ourselves to the list of surfaces on the app.
|
||||||
|
try app.app.addSurface(self);
|
||||||
|
errdefer app.app.deleteSurface(self);
|
||||||
|
|
||||||
|
// Initialize our surface now that we have the stable pointer.
|
||||||
|
try self.core_surface.init(app.app, app.app.config, self);
|
||||||
|
errdefer self.core_surface.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Window) void {
|
pub fn deinit(self: *Surface) void {
|
||||||
var tabgroup_opt: if (builtin.target.isDarwin()) ?objc.Object else void = undefined;
|
var tabgroup_opt: if (builtin.target.isDarwin()) ?objc.Object else void = undefined;
|
||||||
if (comptime builtin.target.isDarwin()) {
|
if (comptime builtin.target.isDarwin()) {
|
||||||
const nswindow = objc.Object.fromId(glfwNative.getCocoaWindow(self.window).?);
|
const nswindow = objc.Object.fromId(glfwNative.getCocoaWindow(self.window).?);
|
||||||
@ -240,7 +274,7 @@ pub const Window = struct {
|
|||||||
|
|
||||||
// If we have a tabgroup set, we want to manually focus the next window.
|
// If we have a tabgroup set, we want to manually focus the next window.
|
||||||
// We should NOT have to do this usually, see the comments above.
|
// We should NOT have to do this usually, see the comments above.
|
||||||
if (comptime builtin.target.isDarwin()) {
|
if (App.Darwin.enabled) {
|
||||||
if (tabgroup_opt) |tabgroup| {
|
if (tabgroup_opt) |tabgroup| {
|
||||||
const selected = tabgroup.getProperty(objc.Object, "selectedWindow");
|
const selected = tabgroup.getProperty(objc.Object, "selectedWindow");
|
||||||
selected.msgSend(void, objc.sel("makeKeyWindow"), .{});
|
selected.msgSend(void, objc.sel("makeKeyWindow"), .{});
|
||||||
@ -252,7 +286,7 @@ pub const Window = struct {
|
|||||||
/// Note: this interface is not good, we should redo it if we plan
|
/// Note: this interface is not good, we should redo it if we plan
|
||||||
/// to use this more. i.e. you can't set max width but no max height,
|
/// to use this more. i.e. you can't set max width but no max height,
|
||||||
/// or no mins.
|
/// or no mins.
|
||||||
pub fn setSizeLimits(self: *Window, min: apprt.WindowSize, max_: ?apprt.WindowSize) !void {
|
pub fn setSizeLimits(self: *Surface, min: apprt.WindowSize, max_: ?apprt.WindowSize) !void {
|
||||||
self.window.setSizeLimits(.{
|
self.window.setSizeLimits(.{
|
||||||
.width = min.width,
|
.width = min.width,
|
||||||
.height = min.height,
|
.height = min.height,
|
||||||
@ -266,7 +300,7 @@ pub const Window = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the content scale for the created window.
|
/// Returns the content scale for the created window.
|
||||||
pub fn getContentScale(self: *const Window) !apprt.ContentScale {
|
pub fn getContentScale(self: *const Surface) !apprt.ContentScale {
|
||||||
const scale = self.window.getContentScale();
|
const scale = self.window.getContentScale();
|
||||||
return apprt.ContentScale{ .x = scale.x_scale, .y = scale.y_scale };
|
return apprt.ContentScale{ .x = scale.x_scale, .y = scale.y_scale };
|
||||||
}
|
}
|
||||||
@ -274,14 +308,14 @@ pub const Window = struct {
|
|||||||
/// Returns the size of the window in pixels. The pixel size may
|
/// Returns the size of the window in pixels. The pixel size may
|
||||||
/// not match screen coordinate size but we should be able to convert
|
/// not match screen coordinate size but we should be able to convert
|
||||||
/// back and forth using getContentScale.
|
/// back and forth using getContentScale.
|
||||||
pub fn getSize(self: *const Window) !apprt.WindowSize {
|
pub fn getSize(self: *const Surface) !apprt.WindowSize {
|
||||||
const size = self.window.getFramebufferSize();
|
const size = self.window.getFramebufferSize();
|
||||||
return apprt.WindowSize{ .width = size.width, .height = size.height };
|
return apprt.WindowSize{ .width = size.width, .height = size.height };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the cursor position in scaled pixels relative to the
|
/// Returns the cursor position in scaled pixels relative to the
|
||||||
/// upper-left of the window.
|
/// upper-left of the window.
|
||||||
pub fn getCursorPos(self: *const Window) !apprt.CursorPos {
|
pub fn getCursorPos(self: *const Surface) !apprt.CursorPos {
|
||||||
const unscaled_pos = self.window.getCursorPos();
|
const unscaled_pos = self.window.getCursorPos();
|
||||||
const pos = try self.cursorPosToPixels(unscaled_pos);
|
const pos = try self.cursorPosToPixels(unscaled_pos);
|
||||||
return apprt.CursorPos{
|
return apprt.CursorPos{
|
||||||
@ -292,37 +326,37 @@ pub const Window = struct {
|
|||||||
|
|
||||||
/// Set the flag that notes this window should be closed for the next
|
/// Set the flag that notes this window should be closed for the next
|
||||||
/// iteration of the event loop.
|
/// iteration of the event loop.
|
||||||
pub fn setShouldClose(self: *Window) void {
|
pub fn setShouldClose(self: *Surface) void {
|
||||||
self.window.setShouldClose(true);
|
self.window.setShouldClose(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the window is flagged to close.
|
/// Returns true if the window is flagged to close.
|
||||||
pub fn shouldClose(self: *const Window) bool {
|
pub fn shouldClose(self: *const Surface) bool {
|
||||||
return self.window.shouldClose();
|
return self.window.shouldClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the title of the window.
|
/// Set the title of the window.
|
||||||
pub fn setTitle(self: *Window, slice: [:0]const u8) !void {
|
pub fn setTitle(self: *Surface, slice: [:0]const u8) !void {
|
||||||
self.window.setTitle(slice.ptr);
|
self.window.setTitle(slice.ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read the clipboard. The windowing system is responsible for allocating
|
/// Read the clipboard. The windowing system is responsible for allocating
|
||||||
/// a buffer as necessary. This should be a stable pointer until the next
|
/// a buffer as necessary. This should be a stable pointer until the next
|
||||||
/// time getClipboardString is called.
|
/// time getClipboardString is called.
|
||||||
pub fn getClipboardString(self: *const Window) ![:0]const u8 {
|
pub fn getClipboardString(self: *const Surface) ![:0]const u8 {
|
||||||
_ = self;
|
_ = self;
|
||||||
return glfw.getClipboardString() orelse return glfw.mustGetErrorCode();
|
return glfw.getClipboardString() orelse return glfw.mustGetErrorCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the clipboard.
|
/// Set the clipboard.
|
||||||
pub fn setClipboardString(self: *const Window, val: [:0]const u8) !void {
|
pub fn setClipboardString(self: *const Surface, val: [:0]const u8) !void {
|
||||||
_ = self;
|
_ = self;
|
||||||
glfw.setClipboardString(val);
|
glfw.setClipboardString(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The cursor position from glfw directly is in screen coordinates but
|
/// The cursor position from glfw directly is in screen coordinates but
|
||||||
/// all our interface works in pixels.
|
/// all our interface works in pixels.
|
||||||
fn cursorPosToPixels(self: *const Window, pos: glfw.Window.CursorPos) !glfw.Window.CursorPos {
|
fn cursorPosToPixels(self: *const Surface, pos: glfw.Window.CursorPos) !glfw.Window.CursorPos {
|
||||||
// The cursor position is in screen coordinates but we
|
// The cursor position is in screen coordinates but we
|
||||||
// want it in pixels. we need to get both the size of the
|
// want it in pixels. we need to get both the size of the
|
||||||
// window in both to get the ratio to make the conversion.
|
// window in both to get the ratio to make the conversion.
|
||||||
@ -349,8 +383,8 @@ pub const Window = struct {
|
|||||||
// Get the size. We are given a width/height but this is in screen
|
// Get the size. We are given a width/height but this is in screen
|
||||||
// coordinates and we want raw pixels. The core window uses the content
|
// coordinates and we want raw pixels. The core window uses the content
|
||||||
// scale to scale appropriately.
|
// scale to scale appropriately.
|
||||||
const core_win = window.getUserPointer(CoreWindow) orelse return;
|
const core_win = window.getUserPointer(CoreSurface) orelse return;
|
||||||
const size = core_win.window.getSize() catch |err| {
|
const size = core_win.rt_surface.getSize() catch |err| {
|
||||||
log.err("error querying window size for size callback err={}", .{err});
|
log.err("error querying window size for size callback err={}", .{err});
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@ -366,7 +400,7 @@ pub const Window = struct {
|
|||||||
const tracy = trace(@src());
|
const tracy = trace(@src());
|
||||||
defer tracy.end();
|
defer tracy.end();
|
||||||
|
|
||||||
const core_win = window.getUserPointer(CoreWindow) orelse return;
|
const core_win = window.getUserPointer(CoreSurface) orelse return;
|
||||||
core_win.charCallback(codepoint) catch |err| {
|
core_win.charCallback(codepoint) catch |err| {
|
||||||
log.err("error in char callback err={}", .{err});
|
log.err("error in char callback err={}", .{err});
|
||||||
return;
|
return;
|
||||||
@ -518,7 +552,7 @@ pub const Window = struct {
|
|||||||
=> .invalid,
|
=> .invalid,
|
||||||
};
|
};
|
||||||
|
|
||||||
const core_win = window.getUserPointer(CoreWindow) orelse return;
|
const core_win = window.getUserPointer(CoreSurface) orelse return;
|
||||||
core_win.keyCallback(action, key, mods) catch |err| {
|
core_win.keyCallback(action, key, mods) catch |err| {
|
||||||
log.err("error in key callback err={}", .{err});
|
log.err("error in key callback err={}", .{err});
|
||||||
return;
|
return;
|
||||||
@ -529,7 +563,7 @@ pub const Window = struct {
|
|||||||
const tracy = trace(@src());
|
const tracy = trace(@src());
|
||||||
defer tracy.end();
|
defer tracy.end();
|
||||||
|
|
||||||
const core_win = window.getUserPointer(CoreWindow) orelse return;
|
const core_win = window.getUserPointer(CoreSurface) orelse return;
|
||||||
core_win.focusCallback(focused) catch |err| {
|
core_win.focusCallback(focused) catch |err| {
|
||||||
log.err("error in focus callback err={}", .{err});
|
log.err("error in focus callback err={}", .{err});
|
||||||
return;
|
return;
|
||||||
@ -540,7 +574,7 @@ pub const Window = struct {
|
|||||||
const tracy = trace(@src());
|
const tracy = trace(@src());
|
||||||
defer tracy.end();
|
defer tracy.end();
|
||||||
|
|
||||||
const core_win = window.getUserPointer(CoreWindow) orelse return;
|
const core_win = window.getUserPointer(CoreSurface) orelse return;
|
||||||
core_win.refreshCallback() catch |err| {
|
core_win.refreshCallback() catch |err| {
|
||||||
log.err("error in refresh callback err={}", .{err});
|
log.err("error in refresh callback err={}", .{err});
|
||||||
return;
|
return;
|
||||||
@ -551,7 +585,7 @@ pub const Window = struct {
|
|||||||
const tracy = trace(@src());
|
const tracy = trace(@src());
|
||||||
defer tracy.end();
|
defer tracy.end();
|
||||||
|
|
||||||
const core_win = window.getUserPointer(CoreWindow) orelse return;
|
const core_win = window.getUserPointer(CoreSurface) orelse return;
|
||||||
core_win.scrollCallback(xoff, yoff) catch |err| {
|
core_win.scrollCallback(xoff, yoff) catch |err| {
|
||||||
log.err("error in scroll callback err={}", .{err});
|
log.err("error in scroll callback err={}", .{err});
|
||||||
return;
|
return;
|
||||||
@ -566,10 +600,10 @@ pub const Window = struct {
|
|||||||
const tracy = trace(@src());
|
const tracy = trace(@src());
|
||||||
defer tracy.end();
|
defer tracy.end();
|
||||||
|
|
||||||
const core_win = window.getUserPointer(CoreWindow) orelse return;
|
const core_win = window.getUserPointer(CoreSurface) orelse return;
|
||||||
|
|
||||||
// Convert our unscaled x/y to scaled.
|
// Convert our unscaled x/y to scaled.
|
||||||
const pos = core_win.window.cursorPosToPixels(.{
|
const pos = core_win.rt_surface.cursorPosToPixels(.{
|
||||||
.xpos = unscaled_xpos,
|
.xpos = unscaled_xpos,
|
||||||
.ypos = unscaled_ypos,
|
.ypos = unscaled_ypos,
|
||||||
}) catch |err| {
|
}) catch |err| {
|
||||||
@ -598,7 +632,7 @@ pub const Window = struct {
|
|||||||
const tracy = trace(@src());
|
const tracy = trace(@src());
|
||||||
defer tracy.end();
|
defer tracy.end();
|
||||||
|
|
||||||
const core_win = window.getUserPointer(CoreWindow) orelse return;
|
const core_win = window.getUserPointer(CoreSurface) orelse return;
|
||||||
|
|
||||||
// Convert glfw button to input button
|
// Convert glfw button to input button
|
||||||
const mods = @bitCast(input.Mods, glfw_mods);
|
const mods = @bitCast(input.Mods, glfw_mods);
|
||||||
|
@ -100,6 +100,7 @@ pub fn main() !void {
|
|||||||
defer app_runtime.terminate();
|
defer app_runtime.terminate();
|
||||||
|
|
||||||
// Create an initial window
|
// Create an initial window
|
||||||
|
try app_runtime.newWindow();
|
||||||
|
|
||||||
// Run the GUI event loop
|
// Run the GUI event loop
|
||||||
try app_runtime.run();
|
try app_runtime.run();
|
||||||
|
@ -18,7 +18,7 @@ const trace = @import("tracy").trace;
|
|||||||
const math = @import("../math.zig");
|
const math = @import("../math.zig");
|
||||||
const lru = @import("../lru.zig");
|
const lru = @import("../lru.zig");
|
||||||
const DevMode = @import("../DevMode.zig");
|
const DevMode = @import("../DevMode.zig");
|
||||||
const Window = @import("../Window.zig");
|
const Surface = @import("../Surface.zig");
|
||||||
|
|
||||||
const log = std.log.scoped(.grid);
|
const log = std.log.scoped(.grid);
|
||||||
|
|
||||||
@ -89,7 +89,7 @@ focused: bool,
|
|||||||
padding: renderer.Options.Padding,
|
padding: renderer.Options.Padding,
|
||||||
|
|
||||||
/// The mailbox for communicating with the window.
|
/// The mailbox for communicating with the window.
|
||||||
window_mailbox: Window.Mailbox,
|
window_mailbox: apprt.surface.Mailbox,
|
||||||
|
|
||||||
/// The raw structure that maps directly to the buffer sent to the vertex shader.
|
/// The raw structure that maps directly to the buffer sent to the vertex shader.
|
||||||
/// This must be "extern" so that the field order is not reordered by the
|
/// This must be "extern" so that the field order is not reordered by the
|
||||||
@ -362,12 +362,11 @@ pub fn glfwWindowHints() glfw.Window.Hints {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is called early right after window creation to setup our
|
/// This is called early right after surface creation.
|
||||||
/// window surface as necessary.
|
pub fn surfaceInit(surface: *apprt.Surface) !void {
|
||||||
pub fn windowInit(win: apprt.runtime.Window) !void {
|
|
||||||
// Treat this like a thread entry
|
// Treat this like a thread entry
|
||||||
const self: OpenGL = undefined;
|
const self: OpenGL = undefined;
|
||||||
try self.threadEnter(win);
|
try self.threadEnter(surface);
|
||||||
|
|
||||||
// Blending for text. We use GL_ONE here because we should be using
|
// Blending for text. We use GL_ONE here because we should be using
|
||||||
// premultiplied alpha for all our colors in our fragment shaders.
|
// premultiplied alpha for all our colors in our fragment shaders.
|
||||||
@ -388,19 +387,19 @@ pub fn windowInit(win: apprt.runtime.Window) !void {
|
|||||||
|
|
||||||
/// This is called just prior to spinning up the renderer thread for
|
/// This is called just prior to spinning up the renderer thread for
|
||||||
/// final main thread setup requirements.
|
/// final main thread setup requirements.
|
||||||
pub fn finalizeWindowInit(self: *const OpenGL, win: apprt.runtime.Window) !void {
|
pub fn finalizeSurfaceInit(self: *const OpenGL, surface: *apprt.Surface) !void {
|
||||||
_ = self;
|
_ = self;
|
||||||
_ = win;
|
_ = surface;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is called if this renderer runs DevMode.
|
/// This is called if this renderer runs DevMode.
|
||||||
pub fn initDevMode(self: *const OpenGL, win: apprt.runtime.Window) !void {
|
pub fn initDevMode(self: *const OpenGL, surface: *apprt.Surface) !void {
|
||||||
_ = self;
|
_ = self;
|
||||||
|
|
||||||
if (DevMode.enabled) {
|
if (DevMode.enabled) {
|
||||||
// Initialize for our window
|
// Initialize for our window
|
||||||
assert(imgui.ImplGlfw.initForOpenGL(
|
assert(imgui.ImplGlfw.initForOpenGL(
|
||||||
@ptrCast(*imgui.ImplGlfw.GLFWWindow, win.window.handle),
|
@ptrCast(*imgui.ImplGlfw.GLFWWindow, surface.window.handle),
|
||||||
true,
|
true,
|
||||||
));
|
));
|
||||||
assert(imgui.ImplOpenGL3.init("#version 330 core"));
|
assert(imgui.ImplOpenGL3.init("#version 330 core"));
|
||||||
@ -418,7 +417,7 @@ pub fn deinitDevMode(self: *const OpenGL) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Callback called by renderer.Thread when it begins.
|
/// Callback called by renderer.Thread when it begins.
|
||||||
pub fn threadEnter(self: *const OpenGL, win: apprt.runtime.Window) !void {
|
pub fn threadEnter(self: *const OpenGL, surface: *apprt.Surface) !void {
|
||||||
_ = self;
|
_ = self;
|
||||||
|
|
||||||
switch (apprt.runtime) {
|
switch (apprt.runtime) {
|
||||||
@ -437,7 +436,7 @@ pub fn threadEnter(self: *const OpenGL, win: apprt.runtime.Window) !void {
|
|||||||
// ensures that the context switches over to our thread. Important:
|
// ensures that the context switches over to our thread. Important:
|
||||||
// the prior thread MUST have detached the context prior to calling
|
// the prior thread MUST have detached the context prior to calling
|
||||||
// this entrypoint.
|
// this entrypoint.
|
||||||
glfw.makeContextCurrent(win.window);
|
glfw.makeContextCurrent(surface.window);
|
||||||
errdefer glfw.makeContextCurrent(null);
|
errdefer glfw.makeContextCurrent(null);
|
||||||
glfw.swapInterval(1);
|
glfw.swapInterval(1);
|
||||||
|
|
||||||
@ -548,7 +547,7 @@ fn resetFontMetrics(
|
|||||||
/// The primary render callback that is completely thread-safe.
|
/// The primary render callback that is completely thread-safe.
|
||||||
pub fn render(
|
pub fn render(
|
||||||
self: *OpenGL,
|
self: *OpenGL,
|
||||||
win: apprt.runtime.Window,
|
surface: *apprt.Surface,
|
||||||
state: *renderer.State,
|
state: *renderer.State,
|
||||||
) !void {
|
) !void {
|
||||||
// Data we extract out of the critical area.
|
// Data we extract out of the critical area.
|
||||||
@ -669,7 +668,7 @@ pub fn render(
|
|||||||
|
|
||||||
// Swap our window buffers
|
// Swap our window buffers
|
||||||
if (apprt.runtime == apprt.gtk) @panic("TODO");
|
if (apprt.runtime == apprt.gtk) @panic("TODO");
|
||||||
win.window.swapBuffers();
|
surface.window.swapBuffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// rebuildCells rebuilds all the GPU cells from our CPU state. This is a
|
/// rebuildCells rebuilds all the GPU cells from our CPU state. This is a
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
//! The options that are used to configure a renderer.
|
//! The options that are used to configure a renderer.
|
||||||
|
|
||||||
|
const apprt = @import("../apprt.zig");
|
||||||
const font = @import("../font/main.zig");
|
const font = @import("../font/main.zig");
|
||||||
const renderer = @import("../renderer.zig");
|
const renderer = @import("../renderer.zig");
|
||||||
const Window = @import("../Window.zig");
|
|
||||||
const Config = @import("../config.zig").Config;
|
const Config = @import("../config.zig").Config;
|
||||||
|
|
||||||
/// The app configuration.
|
/// The app configuration.
|
||||||
@ -16,7 +16,7 @@ padding: Padding,
|
|||||||
|
|
||||||
/// The mailbox for sending the window messages. This is only valid
|
/// The mailbox for sending the window messages. This is only valid
|
||||||
/// once the thread has started and should not be used outside of the thread.
|
/// once the thread has started and should not be used outside of the thread.
|
||||||
window_mailbox: Window.Mailbox,
|
window_mailbox: apprt.surface.Mailbox,
|
||||||
|
|
||||||
pub const Padding = struct {
|
pub const Padding = struct {
|
||||||
// Explicit padding options, in pixels. The windowing thread is
|
// Explicit padding options, in pixels. The windowing thread is
|
||||||
|
@ -47,8 +47,8 @@ cursor_h: xev.Timer,
|
|||||||
cursor_c: xev.Completion = .{},
|
cursor_c: xev.Completion = .{},
|
||||||
cursor_c_cancel: xev.Completion = .{},
|
cursor_c_cancel: xev.Completion = .{},
|
||||||
|
|
||||||
/// The window we're rendering to.
|
/// The surface we're rendering to.
|
||||||
window: apprt.runtime.Window,
|
surface: *apprt.Surface,
|
||||||
|
|
||||||
/// The underlying renderer implementation.
|
/// The underlying renderer implementation.
|
||||||
renderer: *renderer.Renderer,
|
renderer: *renderer.Renderer,
|
||||||
@ -65,7 +65,7 @@ mailbox: *Mailbox,
|
|||||||
/// is up to the caller to start the thread with the threadMain entrypoint.
|
/// is up to the caller to start the thread with the threadMain entrypoint.
|
||||||
pub fn init(
|
pub fn init(
|
||||||
alloc: Allocator,
|
alloc: Allocator,
|
||||||
win: apprt.runtime.Window,
|
surface: *apprt.Surface,
|
||||||
renderer_impl: *renderer.Renderer,
|
renderer_impl: *renderer.Renderer,
|
||||||
state: *renderer.State,
|
state: *renderer.State,
|
||||||
) !Thread {
|
) !Thread {
|
||||||
@ -100,7 +100,7 @@ pub fn init(
|
|||||||
.stop = stop_h,
|
.stop = stop_h,
|
||||||
.render_h = render_h,
|
.render_h = render_h,
|
||||||
.cursor_h = cursor_timer,
|
.cursor_h = cursor_timer,
|
||||||
.window = win,
|
.surface = surface,
|
||||||
.renderer = renderer_impl,
|
.renderer = renderer_impl,
|
||||||
.state = state,
|
.state = state,
|
||||||
.mailbox = mailbox,
|
.mailbox = mailbox,
|
||||||
@ -135,7 +135,7 @@ fn threadMain_(self: *Thread) !void {
|
|||||||
// Run our thread start/end callbacks. This is important because some
|
// Run our thread start/end callbacks. This is important because some
|
||||||
// renderers have to do per-thread setup. For example, OpenGL has to set
|
// renderers have to do per-thread setup. For example, OpenGL has to set
|
||||||
// some thread-local state since that is how it works.
|
// some thread-local state since that is how it works.
|
||||||
try self.renderer.threadEnter(self.window);
|
try self.renderer.threadEnter(self.surface);
|
||||||
defer self.renderer.threadExit();
|
defer self.renderer.threadExit();
|
||||||
|
|
||||||
// Start the async handlers
|
// Start the async handlers
|
||||||
@ -305,7 +305,7 @@ fn renderCallback(
|
|||||||
return .disarm;
|
return .disarm;
|
||||||
};
|
};
|
||||||
|
|
||||||
t.renderer.render(t.window, t.state) catch |err|
|
t.renderer.render(t.surface, t.state) catch |err|
|
||||||
log.warn("error rendering err={}", .{err});
|
log.warn("error rendering err={}", .{err});
|
||||||
return .disarm;
|
return .disarm;
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ const assert = std.debug.assert;
|
|||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const termio = @import("../termio.zig");
|
const termio = @import("../termio.zig");
|
||||||
const Command = @import("../Command.zig");
|
const Command = @import("../Command.zig");
|
||||||
const Window = @import("../Window.zig");
|
|
||||||
const Pty = @import("../Pty.zig");
|
const Pty = @import("../Pty.zig");
|
||||||
const SegmentedPool = @import("../segmented_pool.zig").SegmentedPool;
|
const SegmentedPool = @import("../segmented_pool.zig").SegmentedPool;
|
||||||
const terminal = @import("../terminal/main.zig");
|
const terminal = @import("../terminal/main.zig");
|
||||||
@ -16,6 +15,7 @@ const xev = @import("xev");
|
|||||||
const renderer = @import("../renderer.zig");
|
const renderer = @import("../renderer.zig");
|
||||||
const tracy = @import("tracy");
|
const tracy = @import("tracy");
|
||||||
const trace = tracy.trace;
|
const trace = tracy.trace;
|
||||||
|
const apprt = @import("../apprt.zig");
|
||||||
const fastmem = @import("../fastmem.zig");
|
const fastmem = @import("../fastmem.zig");
|
||||||
|
|
||||||
const log = std.log.scoped(.io_exec);
|
const log = std.log.scoped(.io_exec);
|
||||||
@ -52,7 +52,7 @@ renderer_wakeup: xev.Async,
|
|||||||
renderer_mailbox: *renderer.Thread.Mailbox,
|
renderer_mailbox: *renderer.Thread.Mailbox,
|
||||||
|
|
||||||
/// The mailbox for communicating with the window.
|
/// The mailbox for communicating with the window.
|
||||||
window_mailbox: Window.Mailbox,
|
window_mailbox: apprt.surface.Mailbox,
|
||||||
|
|
||||||
/// The cached grid size whenever a resize is called.
|
/// The cached grid size whenever a resize is called.
|
||||||
grid_size: renderer.GridSize,
|
grid_size: renderer.GridSize,
|
||||||
@ -638,7 +638,7 @@ const StreamHandler = struct {
|
|||||||
alloc: Allocator,
|
alloc: Allocator,
|
||||||
grid_size: *renderer.GridSize,
|
grid_size: *renderer.GridSize,
|
||||||
terminal: *terminal.Terminal,
|
terminal: *terminal.Terminal,
|
||||||
window_mailbox: Window.Mailbox,
|
window_mailbox: apprt.surface.Mailbox,
|
||||||
|
|
||||||
/// This is set to true when a message was written to the writer
|
/// This is set to true when a message was written to the writer
|
||||||
/// mailbox. This can be used by callers to determine if they need
|
/// mailbox. This can be used by callers to determine if they need
|
||||||
@ -1003,7 +1003,7 @@ const StreamHandler = struct {
|
|||||||
|
|
||||||
// Write clipboard contents
|
// Write clipboard contents
|
||||||
_ = self.window_mailbox.push(.{
|
_ = self.window_mailbox.push(.{
|
||||||
.clipboard_write = try Window.Message.WriteReq.init(
|
.clipboard_write = try apprt.surface.Message.WriteReq.init(
|
||||||
self.alloc,
|
self.alloc,
|
||||||
data,
|
data,
|
||||||
),
|
),
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
//! The options that are used to configure a terminal IO implementation.
|
//! The options that are used to configure a terminal IO implementation.
|
||||||
|
|
||||||
const xev = @import("xev");
|
const xev = @import("xev");
|
||||||
|
const apprt = @import("../apprt.zig");
|
||||||
const renderer = @import("../renderer.zig");
|
const renderer = @import("../renderer.zig");
|
||||||
const Config = @import("../config.zig").Config;
|
const Config = @import("../config.zig").Config;
|
||||||
const Window = @import("../Window.zig");
|
|
||||||
|
|
||||||
/// The size of the terminal grid.
|
/// The size of the terminal grid.
|
||||||
grid_size: renderer.GridSize,
|
grid_size: renderer.GridSize,
|
||||||
@ -28,4 +28,4 @@ renderer_wakeup: xev.Async,
|
|||||||
renderer_mailbox: *renderer.Thread.Mailbox,
|
renderer_mailbox: *renderer.Thread.Mailbox,
|
||||||
|
|
||||||
/// The mailbox for sending the window messages.
|
/// The mailbox for sending the window messages.
|
||||||
window_mailbox: Window.Mailbox,
|
window_mailbox: apprt.surface.Mailbox,
|
||||||
|
Reference in New Issue
Block a user