window: start abstracting a window implementation

This commit is contained in:
Mitchell Hashimoto
2022-12-29 14:37:18 -08:00
parent e253d12a7d
commit 6eb5a0238a
6 changed files with 146 additions and 17 deletions

View File

@ -30,6 +30,7 @@ const input = @import("input.zig");
const DevMode = @import("DevMode.zig");
const App = @import("App.zig");
const internal_os = @import("os/main.zig");
const WindowingSystem = @import("window.zig").System;
// Get native API access on certain platforms so we can do more customization.
const glfwNative = glfw.Native(.{
@ -47,6 +48,9 @@ alloc: Allocator,
/// The app that this window is a part of.
app: *App,
/// The windowing system state
windowing_system: WindowingSystem,
/// The font structures
font_lib: font.Library,
font_group: *font.GroupCache,
@ -135,6 +139,10 @@ pub fn create(alloc: Allocator, app: *App, config: *const Config) !*Window {
var self = try alloc.create(Window);
errdefer alloc.destroy(self);
// Create the windowing system
var winsys = try WindowingSystem.init(app);
winsys.deinit();
// Create our window
const window = try glfw.Window.create(640, 480, "ghostty", null, null, Renderer.windowHints());
errdefer window.destroy();
@ -153,6 +161,16 @@ pub fn create(alloc: Allocator, app: *App, config: *const Config) !*Window {
nswindow.setProperty("tabbingIdentifier", app.darwin.tabbing_id);
}
// Create the cursor
const cursor = try glfw.Cursor.createStandard(.ibeam);
errdefer cursor.destroy();
if ((comptime !builtin.target.isDarwin()) or internal_os.macosVersionAtLeast(13, 0, 0)) {
// We only set our cursor if we're NOT on Mac, or if we are then the
// macOS version is >= 13 (Ventura). On prior versions, glfw crashes
// since we use a tab group.
try window.setCursor(cursor);
}
// Determine our DPI configurations so we can properly configure
// font points to pixels and handle other high-DPI scaling factors.
const content_scale = try window.getContentScale();
@ -322,23 +340,6 @@ pub fn create(alloc: Allocator, app: *App, config: *const Config) !*Window {
cell_size,
);
// Set a minimum size that is cols=10 h=4. This matches Mac's Terminal.app
// but is otherwise somewhat arbitrary.
try window.setSizeLimits(.{
.width = @floatToInt(u32, cell_size.width * 10),
.height = @floatToInt(u32, cell_size.height * 4),
}, .{ .width = null, .height = null });
// Create the cursor
const cursor = try glfw.Cursor.createStandard(.ibeam);
errdefer cursor.destroy();
if ((comptime !builtin.target.isDarwin()) or internal_os.macosVersionAtLeast(13, 0, 0)) {
// We only set our cursor if we're NOT on Mac, or if we are then the
// macOS version is >= 13 (Ventura). On prior versions, glfw crashes
// since we use a tab group.
try window.setCursor(cursor);
}
// The mutex used to protect our renderer state.
var mutex = try alloc.create(std.Thread.Mutex);
mutex.* = .{};
@ -377,6 +378,7 @@ pub fn create(alloc: Allocator, app: *App, config: *const Config) !*Window {
self.* = .{
.alloc = alloc,
.app = app,
.windowing_system = winsys,
.font_lib = font_lib,
.font_group = font_group,
.font_size = font_size,
@ -409,6 +411,13 @@ pub fn create(alloc: Allocator, app: *App, config: *const Config) !*Window {
};
errdefer if (DevMode.enabled) self.imgui_ctx.destroy();
// Set a minimum size that is cols=10 h=4. This matches Mac's Terminal.app
// but is otherwise somewhat arbitrary.
try window.setSizeLimits(.{
.width = @floatToInt(u32, cell_size.width * 10),
.height = @floatToInt(u32, cell_size.height * 4),
}, .{ .width = null, .height = null });
// Setup our callbacks and user data
window.setUserPointer(self);
window.setSizeCallback(sizeCallback);

18
src/window.zig Normal file
View File

@ -0,0 +1,18 @@
//! Window implementation and utilities. The window subsystem is responsible
//! for maintaining a "window" or "surface" abstraction around a terminal,
//! effectively being the primary interface to the terminal.
const builtin = @import("builtin");
pub usingnamespace @import("window/structs.zig");
pub const Glfw = @import("window/Glfw.zig");
/// The implementation to use for the windowing system. This is comptime chosen
/// so that every build has exactly one windowing implementation.
pub const System = switch (builtin.os.tag) {
else => Glfw,
};
test {
@import("std").testing.refAllDecls(@This());
}

77
src/window/Glfw.zig Normal file
View File

@ -0,0 +1,77 @@
//! Window implementation that uses GLFW (https://www.glfw.org/).
pub const Glfw = @This();
const std = @import("std");
const builtin = @import("builtin");
const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
const glfw = @import("glfw");
const objc = @import("objc");
const App = @import("../App.zig");
const internal_os = @import("../os/main.zig");
const renderer = @import("../renderer.zig");
const Renderer = renderer.Renderer;
const window = @import("../window.zig");
// Get native API access on certain platforms so we can do more customization.
const glfwNative = glfw.Native(.{
.cocoa = builtin.target.isDarwin(),
});
/// The glfw window handle
window: glfw.Window,
/// The glfw mouse cursor handle.
cursor: glfw.Cursor,
pub fn init(app: *const App) !Glfw {
// Create our window
const win = try glfw.Window.create(640, 480, "ghostty", null, null, Renderer.windowHints());
errdefer win.destroy();
try Renderer.windowInit(win);
// On Mac, enable tabbing
if (comptime builtin.target.isDarwin()) {
const NSWindowTabbingMode = enum(usize) { automatic = 0, preferred = 1, disallowed = 2 };
const nswindow = objc.Object.fromId(glfwNative.getCocoaWindow(win).?);
// Tabbing mode enables tabbing at all
nswindow.setProperty("tabbingMode", NSWindowTabbingMode.automatic);
// All windows within a tab bar must have a matching tabbing ID.
// The app sets this up for us.
nswindow.setProperty("tabbingIdentifier", app.darwin.tabbing_id);
}
// Create the cursor
const cursor = try glfw.Cursor.createStandard(.ibeam);
errdefer cursor.destroy();
if ((comptime !builtin.target.isDarwin()) or internal_os.macosVersionAtLeast(13, 0, 0)) {
// We only set our cursor if we're NOT on Mac, or if we are then the
// macOS version is >= 13 (Ventura). On prior versions, glfw crashes
// since we use a tab group.
try win.setCursor(cursor);
}
return Glfw{
.window = win,
.cursor = cursor,
};
}
pub fn deinit(self: *Glfw) void {
self.window.destroy();
self.cursor.destroy();
}
/// Returns the content scale for the created window.
pub fn getContentScale(self: *const Glfw) !window.ContentScale {
const scale = try self.window.getContentScale();
return window.ContentScale{ .x = scale.x_scale, .y = scale.y_scale };
}
/// Returns the size of the window in screen coordinates.
pub fn getSize(self: *const Glfw) !window.Size {
const size = try self.window.getSize();
return window.Size{ .width = size.width, .height = size.height };
}

2
src/window/Web.zig Normal file
View File

@ -0,0 +1,2 @@
//! Window implementation for the web (browser) via WebAssembly.
pub const Window = @This();

10
src/window/Window.zig Normal file
View File

@ -0,0 +1,10 @@
//! Window represents a single terminal window. A terminal window is
//! a single drawable terminal surface.
//!
//! This Window is the abstract window logic that applies to all platforms.
//! Platforms are expected to implement a compile-time "interface" to
//! implement platform-specific logic.
//!
//! Note(mitchellh): We current conflate a "window" and a "surface". If
//! we implement splits, we probably will need to separate these concepts.
pub const Window = @This();

13
src/window/structs.zig Normal file
View File

@ -0,0 +1,13 @@
/// ContentScale is the ratio between the current DPI and the platform's
/// default DPI. This is used to determine how much certain rendered elements
/// need to be scaled up or down.
pub const ContentScale = struct {
x: f32,
y: f32,
};
/// The size of the window in screen coordinates.
pub const Size = struct {
width: u32,
height: u32,
};