Merge 2ad00dc628 into 629838b9bd
commit
e2b1e9e1e2
|
|
@ -61,6 +61,12 @@
|
|||
.hash = "gobject-0.3.0-Skun7ANLnwDvEfIpVmohcppXgOvg_I6YOJFmPIsKfXk-",
|
||||
.lazy = true,
|
||||
},
|
||||
.win32 = .{
|
||||
// marlersoft/zigwin32 -- Win32 API bindings for the win32 apprt.
|
||||
.url = "git+https://github.com/marlersoft/zigwin32#ec98bb4d9eea532320a8551720a9e3ec6de64994",
|
||||
.hash = "win32-25.0.28-preview-mX5pFWMt5QPTVIGh3r2-OpPunpcCCjApyRbA6Zn6WALH",
|
||||
.lazy = true,
|
||||
},
|
||||
|
||||
// C libs
|
||||
.dcimgui = .{ .path = "./pkg/dcimgui", .lazy = true },
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ pub const action = @import("apprt/action.zig");
|
|||
pub const ipc = @import("apprt/ipc.zig");
|
||||
pub const gtk = @import("apprt/gtk.zig");
|
||||
pub const none = @import("apprt/none.zig");
|
||||
pub const win32 = @import("apprt/win32.zig");
|
||||
pub const browser = @import("apprt/browser.zig");
|
||||
pub const embedded = @import("apprt/embedded.zig");
|
||||
pub const surface = @import("apprt/surface.zig");
|
||||
|
|
@ -43,6 +44,7 @@ pub const runtime = switch (build_config.artifact) {
|
|||
.exe => switch (build_config.app_runtime) {
|
||||
.none => none,
|
||||
.gtk => gtk,
|
||||
.win32 => win32,
|
||||
},
|
||||
.lib => embedded,
|
||||
.wasm_module => browser,
|
||||
|
|
|
|||
|
|
@ -11,11 +11,17 @@ pub const Runtime = enum {
|
|||
/// approach to building the application.
|
||||
gtk,
|
||||
|
||||
/// Win32. Native Windows application using the Win32 API with OpenGL
|
||||
/// rendering.
|
||||
win32,
|
||||
|
||||
pub fn default(target: std.Target) Runtime {
|
||||
return switch (target.os.tag) {
|
||||
// The Linux and FreeBSD default is GTK because it is a full
|
||||
// featured application.
|
||||
.linux, .freebsd => .gtk,
|
||||
// Windows uses the native Win32 API.
|
||||
.windows => .win32,
|
||||
// Otherwise, we do NONE so we don't create an exe and we create
|
||||
// libghostty. On macOS, Xcode is used to build the app that links
|
||||
// to libghostty.
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ pub const Clipboard = enum(Backing) {
|
|||
.{ .name = "GhosttyApprtClipboard" },
|
||||
),
|
||||
|
||||
.none => void,
|
||||
.none, .win32 => void,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
// The required comptime API for any apprt.
|
||||
pub const App = @import("win32/App.zig");
|
||||
pub const Surface = @import("win32/Surface.zig");
|
||||
pub const resourcesDir = @import("../os/main.zig").resourcesDir;
|
||||
|
||||
test {
|
||||
@import("std").testing.refAllDecls(@This());
|
||||
}
|
||||
|
|
@ -0,0 +1,294 @@
|
|||
/// Win32 application runtime for Ghostty. This is a minimal native Windows
|
||||
/// application using the Win32 API with OpenGL rendering.
|
||||
const App = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const win32 = @import("win32").everything;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const apprt = @import("../../apprt.zig");
|
||||
const configpkg = @import("../../config.zig");
|
||||
const Config = configpkg.Config;
|
||||
const CoreApp = @import("../../App.zig");
|
||||
const CoreSurface = @import("../../Surface.zig");
|
||||
const Surface = @import("Surface.zig");
|
||||
const renderer = @import("../../renderer.zig");
|
||||
|
||||
const log = std.log.scoped(.win32);
|
||||
|
||||
/// User-defined wakeup message sent via PostMessage to break out of
|
||||
/// GetMessage and run the core app's tick.
|
||||
const WM_WAKEUP = win32.WM_USER + 1;
|
||||
|
||||
/// The core app instance.
|
||||
core_app: *CoreApp,
|
||||
|
||||
/// The configuration.
|
||||
config: *Config,
|
||||
|
||||
/// The allocator.
|
||||
alloc: Allocator,
|
||||
|
||||
/// Whether the app is running.
|
||||
running: bool = true,
|
||||
|
||||
/// The main window handle.
|
||||
hwnd: ?win32.HWND = null,
|
||||
|
||||
/// The surface for the main window.
|
||||
surface: Surface = undefined,
|
||||
|
||||
pub fn init(
|
||||
self: *App,
|
||||
core_app: *CoreApp,
|
||||
opts: struct {},
|
||||
) !void {
|
||||
_ = opts;
|
||||
|
||||
const alloc = core_app.alloc;
|
||||
|
||||
// Load configuration
|
||||
var config = try Config.load(alloc);
|
||||
errdefer config.deinit();
|
||||
|
||||
const config_ptr = try alloc.create(Config);
|
||||
config_ptr.* = config;
|
||||
|
||||
self.* = .{
|
||||
.core_app = core_app,
|
||||
.config = config_ptr,
|
||||
.alloc = alloc,
|
||||
};
|
||||
|
||||
// Create the main window
|
||||
try self.createWindow();
|
||||
|
||||
// Initialize the surface with OpenGL
|
||||
try self.surface.init(self.hwnd.?);
|
||||
|
||||
// Store self pointer in window for use in wndProc. SetWindowLongPtrW
|
||||
// returns the previous value, which for a freshly created window is 0;
|
||||
// we don't care about it here.
|
||||
_ = win32.SetWindowLongPtrW(
|
||||
self.hwnd.?,
|
||||
win32.GWLP_USERDATA,
|
||||
@bitCast(@intFromPtr(self)),
|
||||
);
|
||||
|
||||
// Initialize the core surface (terminal emulation + rendering)
|
||||
try self.initCoreSurface();
|
||||
}
|
||||
|
||||
pub fn run(self: *App) !void {
|
||||
log.info("starting Win32 event loop", .{});
|
||||
|
||||
while (self.running) {
|
||||
var msg: win32.MSG = std.mem.zeroes(win32.MSG);
|
||||
const ret = win32.GetMessageW(&msg, null, 0, 0);
|
||||
if (ret == 0) {
|
||||
// WM_QUIT
|
||||
self.running = false;
|
||||
break;
|
||||
}
|
||||
if (ret == -1) {
|
||||
log.err("GetMessage failed: err={d}", .{@intFromEnum(win32.GetLastError())});
|
||||
return error.Win32Error;
|
||||
}
|
||||
_ = win32.TranslateMessage(&msg);
|
||||
_ = win32.DispatchMessageW(&msg);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn terminate(self: *App) void {
|
||||
self.surface.deinit();
|
||||
if (self.hwnd) |hwnd| {
|
||||
if (win32.DestroyWindow(hwnd) == 0) {
|
||||
log.warn("DestroyWindow failed: err={d}", .{@intFromEnum(win32.GetLastError())});
|
||||
}
|
||||
self.hwnd = null;
|
||||
}
|
||||
self.config.deinit();
|
||||
self.alloc.destroy(self.config);
|
||||
}
|
||||
|
||||
pub fn wakeup(self: *App) void {
|
||||
if (self.hwnd) |hwnd| {
|
||||
if (win32.PostMessageW(hwnd, WM_WAKEUP, 0, 0) == 0) {
|
||||
log.warn("PostMessage(WM_WAKEUP) failed: err={d}", .{@intFromEnum(win32.GetLastError())});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn performAction(
|
||||
self: *App,
|
||||
target: apprt.Target,
|
||||
comptime action: apprt.Action.Key,
|
||||
value: apprt.Action.Value(action),
|
||||
) !bool {
|
||||
_ = self;
|
||||
_ = target;
|
||||
_ = value;
|
||||
|
||||
switch (action) {
|
||||
.quit => {
|
||||
win32.PostQuitMessage(0);
|
||||
return true;
|
||||
},
|
||||
.new_window => {
|
||||
// TODO: implement multiple windows
|
||||
return false;
|
||||
},
|
||||
else => return false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn performIpc(
|
||||
_: Allocator,
|
||||
_: apprt.ipc.Target,
|
||||
comptime action: apprt.ipc.Action.Key,
|
||||
_: apprt.ipc.Action.Value(action),
|
||||
) !bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn redrawInspector(_: *App, surface: *Surface) void {
|
||||
surface.redrawInspector();
|
||||
}
|
||||
|
||||
fn initCoreSurface(self: *App) !void {
|
||||
const alloc = self.alloc;
|
||||
|
||||
// Set the app pointer on the surface
|
||||
self.surface.app = self;
|
||||
|
||||
// Create the core surface
|
||||
const core_surface = try alloc.create(CoreSurface);
|
||||
errdefer alloc.destroy(core_surface);
|
||||
|
||||
// Register with the core app
|
||||
try self.core_app.addSurface(&self.surface);
|
||||
errdefer self.core_app.deleteSurface(&self.surface);
|
||||
|
||||
// Create a surface config
|
||||
var config = try apprt.surface.newConfig(
|
||||
self.core_app,
|
||||
self.config,
|
||||
.window,
|
||||
);
|
||||
defer config.deinit();
|
||||
|
||||
// Initialize the core surface
|
||||
core_surface.init(
|
||||
alloc,
|
||||
&config,
|
||||
self.core_app,
|
||||
self,
|
||||
&self.surface,
|
||||
) catch |err| {
|
||||
log.err("failed to initialize core surface: {}", .{err});
|
||||
return err;
|
||||
};
|
||||
|
||||
self.surface.core_surface = core_surface;
|
||||
log.info("core surface initialized successfully", .{});
|
||||
}
|
||||
|
||||
fn createWindow(self: *App) !void {
|
||||
const class_name = win32.L("GhosttyWindow");
|
||||
const hinstance = win32.GetModuleHandleW(null);
|
||||
|
||||
const wc: win32.WNDCLASSEXW = .{
|
||||
.cbSize = @sizeOf(win32.WNDCLASSEXW),
|
||||
.style = .{ .HREDRAW = 1, .VREDRAW = 1, .OWNDC = 1 },
|
||||
.lpfnWndProc = wndProc,
|
||||
.cbClsExtra = 0,
|
||||
.cbWndExtra = 0,
|
||||
.hInstance = hinstance,
|
||||
.hIcon = null,
|
||||
.hCursor = win32.LoadCursorW(null, win32.IDC_ARROW),
|
||||
.hbrBackground = null,
|
||||
.lpszMenuName = null,
|
||||
.lpszClassName = class_name,
|
||||
.hIconSm = null,
|
||||
};
|
||||
|
||||
if (win32.RegisterClassExW(&wc) == 0) {
|
||||
log.err("RegisterClassExW failed: err={d}", .{@intFromEnum(win32.GetLastError())});
|
||||
return error.Win32Error;
|
||||
}
|
||||
|
||||
const title = win32.L("Ghostty");
|
||||
|
||||
self.hwnd = win32.CreateWindowExW(
|
||||
.{},
|
||||
class_name,
|
||||
title,
|
||||
win32.WS_OVERLAPPEDWINDOW,
|
||||
win32.CW_USEDEFAULT,
|
||||
win32.CW_USEDEFAULT,
|
||||
800,
|
||||
600,
|
||||
null,
|
||||
null,
|
||||
hinstance,
|
||||
null,
|
||||
) orelse {
|
||||
log.err("CreateWindowExW failed: err={d}", .{@intFromEnum(win32.GetLastError())});
|
||||
return error.Win32Error;
|
||||
};
|
||||
|
||||
_ = win32.ShowWindow(self.hwnd.?, win32.SW_SHOWNORMAL);
|
||||
_ = win32.UpdateWindow(self.hwnd.?);
|
||||
}
|
||||
|
||||
fn getApp(hwnd: win32.HWND) ?*App {
|
||||
const ptr = win32.GetWindowLongPtrW(hwnd, win32.GWLP_USERDATA);
|
||||
if (ptr == 0) return null;
|
||||
return @ptrFromInt(@as(usize, @bitCast(ptr)));
|
||||
}
|
||||
|
||||
fn wndProc(hwnd: win32.HWND, msg: u32, wparam: win32.WPARAM, lparam: win32.LPARAM) callconv(.winapi) win32.LRESULT {
|
||||
switch (msg) {
|
||||
win32.WM_CLOSE => {
|
||||
win32.PostQuitMessage(0);
|
||||
return 0;
|
||||
},
|
||||
win32.WM_SIZE => {
|
||||
if (getApp(hwnd)) |app| {
|
||||
const width: u32 = @intCast(lparam & 0xFFFF);
|
||||
const height: u32 = @intCast((lparam >> 16) & 0xFFFF);
|
||||
if (width > 0 and height > 0) {
|
||||
app.surface.width = width;
|
||||
app.surface.height = height;
|
||||
if (app.surface.core_surface) |core| {
|
||||
core.sizeCallback(.{
|
||||
.width = width,
|
||||
.height = height,
|
||||
}) catch |err| {
|
||||
log.err("size callback error: {}", .{err});
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
win32.WM_PAINT => {
|
||||
var ps: win32.PAINTSTRUCT = std.mem.zeroes(win32.PAINTSTRUCT);
|
||||
_ = win32.BeginPaint(hwnd, &ps);
|
||||
if (getApp(hwnd)) |app| {
|
||||
app.surface.swapBuffers();
|
||||
}
|
||||
_ = win32.EndPaint(hwnd, &ps);
|
||||
return 0;
|
||||
},
|
||||
WM_WAKEUP => {
|
||||
if (getApp(hwnd)) |app| {
|
||||
app.core_app.tick(app) catch |err| {
|
||||
log.err("core app tick failed: {}", .{err});
|
||||
};
|
||||
}
|
||||
return 0;
|
||||
},
|
||||
else => return win32.DefWindowProcW(hwnd, msg, wparam, lparam),
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,192 @@
|
|||
/// Win32 surface - represents a terminal surface within a window.
|
||||
/// Manages the WGL OpenGL context and provides the interface
|
||||
/// expected by CoreSurface.
|
||||
const Self = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const win32 = @import("win32").everything;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const apprt = @import("../../apprt.zig");
|
||||
const configpkg = @import("../../config.zig");
|
||||
const CoreSurface = @import("../../Surface.zig");
|
||||
const CoreApp = @import("../../App.zig");
|
||||
|
||||
const log = std.log.scoped(.win32_surface);
|
||||
|
||||
/// The window this surface belongs to.
|
||||
hwnd: win32.HWND,
|
||||
|
||||
/// Pointer back to the App.
|
||||
app: ?*App = null,
|
||||
|
||||
/// GDI device context.
|
||||
hdc: ?win32.HDC = null,
|
||||
|
||||
/// OpenGL rendering context.
|
||||
hglrc: ?win32.HGLRC = null,
|
||||
|
||||
/// The core surface, if initialized.
|
||||
core_surface: ?*CoreSurface = null,
|
||||
|
||||
/// Window dimensions.
|
||||
width: u32 = 800,
|
||||
height: u32 = 600,
|
||||
|
||||
const App = @import("App.zig");
|
||||
|
||||
pub fn core(self: *Self) *CoreSurface {
|
||||
return self.core_surface.?;
|
||||
}
|
||||
|
||||
pub fn rtApp(self: *Self) *App {
|
||||
return self.app.?;
|
||||
}
|
||||
|
||||
pub fn init(self: *Self, hwnd: win32.HWND) !void {
|
||||
self.* = .{ .hwnd = hwnd };
|
||||
try self.initOpenGL();
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
if (self.core_surface) |surface| {
|
||||
surface.deinit();
|
||||
// core_surface is allocated by CoreApp, freed there
|
||||
}
|
||||
if (self.hglrc) |hglrc| {
|
||||
_ = win32.wglMakeCurrent(null, null);
|
||||
_ = win32.wglDeleteContext(hglrc);
|
||||
}
|
||||
if (self.hdc) |hdc| {
|
||||
_ = win32.ReleaseDC(self.hwnd, hdc);
|
||||
}
|
||||
}
|
||||
|
||||
fn initOpenGL(self: *Self) !void {
|
||||
self.hdc = win32.GetDC(self.hwnd) orelse {
|
||||
log.err("GetDC failed: err={d}", .{@intFromEnum(win32.GetLastError())});
|
||||
return error.Win32Error;
|
||||
};
|
||||
|
||||
var pfd: win32.PIXELFORMATDESCRIPTOR = std.mem.zeroes(win32.PIXELFORMATDESCRIPTOR);
|
||||
pfd.nSize = @sizeOf(win32.PIXELFORMATDESCRIPTOR);
|
||||
pfd.nVersion = 1;
|
||||
pfd.dwFlags = .{ .DRAW_TO_WINDOW = 1, .SUPPORT_OPENGL = 1, .DOUBLEBUFFER = 1 };
|
||||
pfd.iPixelType = .RGBA;
|
||||
pfd.cColorBits = 32;
|
||||
pfd.cDepthBits = 24;
|
||||
pfd.cStencilBits = 8;
|
||||
pfd.iLayerType = .MAIN_PLANE;
|
||||
|
||||
const pixel_format = win32.ChoosePixelFormat(self.hdc, &pfd);
|
||||
if (pixel_format == 0) {
|
||||
log.err("ChoosePixelFormat failed: err={d}", .{@intFromEnum(win32.GetLastError())});
|
||||
return error.Win32Error;
|
||||
}
|
||||
|
||||
if (win32.SetPixelFormat(self.hdc, pixel_format, &pfd) == 0) {
|
||||
log.err("SetPixelFormat failed: err={d}", .{@intFromEnum(win32.GetLastError())});
|
||||
return error.Win32Error;
|
||||
}
|
||||
|
||||
self.hglrc = win32.wglCreateContext(self.hdc) orelse {
|
||||
log.err("wglCreateContext failed: err={d}", .{@intFromEnum(win32.GetLastError())});
|
||||
return error.Win32Error;
|
||||
};
|
||||
|
||||
if (win32.wglMakeCurrent(self.hdc, self.hglrc) == 0) {
|
||||
log.err("wglMakeCurrent failed: err={d}", .{@intFromEnum(win32.GetLastError())});
|
||||
return error.Win32Error;
|
||||
}
|
||||
|
||||
log.info("WGL OpenGL context created successfully", .{});
|
||||
}
|
||||
|
||||
pub fn swapBuffers(self: *Self) void {
|
||||
if (self.hdc) |hdc| {
|
||||
if (win32.SwapBuffers(hdc) == 0) {
|
||||
log.warn("SwapBuffers failed: err={d}", .{@intFromEnum(win32.GetLastError())});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Make the WGL context current on the calling thread.
|
||||
pub fn makeContextCurrent(self: *Self) void {
|
||||
if (self.hdc) |hdc| {
|
||||
if (self.hglrc) |hglrc| {
|
||||
if (win32.wglMakeCurrent(hdc, hglrc) == 0) {
|
||||
log.warn("wglMakeCurrent failed: err={d}", .{@intFromEnum(win32.GetLastError())});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Release the WGL context from the calling thread.
|
||||
pub fn releaseContext() void {
|
||||
if (win32.wglMakeCurrent(null, null) == 0) {
|
||||
log.warn("wglMakeCurrent(null) failed: err={d}", .{@intFromEnum(win32.GetLastError())});
|
||||
}
|
||||
}
|
||||
|
||||
/// Release context from the main thread before handing off to renderer thread.
|
||||
pub fn releaseMainThreadContext(self: *Self) void {
|
||||
_ = self;
|
||||
if (win32.wglMakeCurrent(null, null) == 0) {
|
||||
log.warn("wglMakeCurrent(null) failed: err={d}", .{@intFromEnum(win32.GetLastError())});
|
||||
}
|
||||
}
|
||||
|
||||
// --- Interface methods required by CoreSurface ---
|
||||
|
||||
pub fn getContentScale(_: *const Self) !apprt.ContentScale {
|
||||
// TODO: query DPI from the monitor
|
||||
return .{ .x = 1.0, .y = 1.0 };
|
||||
}
|
||||
|
||||
pub fn getSize(self: *const Self) !apprt.SurfaceSize {
|
||||
return .{
|
||||
.width = self.width,
|
||||
.height = self.height,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getCursorPos(_: *const Self) !apprt.CursorPos {
|
||||
// TODO: track mouse position
|
||||
return .{ .x = 0, .y = 0 };
|
||||
}
|
||||
|
||||
pub fn getTitle(_: *Self) ?[:0]const u8 {
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn close(_: *Self, _: bool) void {
|
||||
// TODO: handle close with confirmation
|
||||
}
|
||||
|
||||
pub fn supportsClipboard(_: *Self, clipboard: apprt.Clipboard) bool {
|
||||
return clipboard == .standard;
|
||||
}
|
||||
|
||||
pub fn clipboardRequest(
|
||||
_: *Self,
|
||||
_: apprt.Clipboard,
|
||||
_: apprt.ClipboardRequest,
|
||||
) !bool {
|
||||
// TODO: implement clipboard read
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn setClipboard(
|
||||
_: *Self,
|
||||
_: apprt.Clipboard,
|
||||
_: []const apprt.ClipboardContent,
|
||||
_: bool,
|
||||
) !void {
|
||||
// TODO: implement clipboard write
|
||||
}
|
||||
|
||||
pub fn defaultTermioEnv(_: *Self) !std.process.EnvMap {
|
||||
// Return an empty env map; the shell will inherit the process env.
|
||||
return std.process.EnvMap.init(std.heap.page_allocator);
|
||||
}
|
||||
|
||||
pub fn redrawInspector(_: *Self) void {}
|
||||
|
|
@ -605,6 +605,14 @@ pub fn add(
|
|||
switch (self.config.app_runtime) {
|
||||
.none => {},
|
||||
.gtk => try self.addGtkNg(step),
|
||||
.win32 => {
|
||||
step.linkSystemLibrary2("user32", .{});
|
||||
step.linkSystemLibrary2("gdi32", .{});
|
||||
step.linkSystemLibrary2("opengl32", .{});
|
||||
if (b.lazyDependency("win32", .{})) |dep| {
|
||||
step.root_module.addImport("win32", dep.module("win32"));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4647,7 +4647,7 @@ pub fn finalize(self: *Config) !void {
|
|||
|
||||
// Apprt-specific defaults
|
||||
switch (build_config.app_runtime) {
|
||||
.none => {},
|
||||
.none, .win32 => {},
|
||||
.gtk => {
|
||||
switch (self.@"gtk-single-instance") {
|
||||
.true, .false => {},
|
||||
|
|
|
|||
|
|
@ -169,6 +169,11 @@ pub fn surfaceInit(surface: *apprt.Surface) !void {
|
|||
apprt.gtk,
|
||||
=> try prepareContext(null),
|
||||
|
||||
// Win32: WGL context is already current, load via null (GLAD
|
||||
// uses opengl32.dll + wglGetProcAddress automatically).
|
||||
apprt.win32,
|
||||
=> try prepareContext(null),
|
||||
|
||||
apprt.embedded => {
|
||||
// TODO(mitchellh): this does nothing today to allow libghostty
|
||||
// to compile for OpenGL targets but libghostty is strictly
|
||||
|
|
@ -190,48 +195,39 @@ pub fn surfaceInit(surface: *apprt.Surface) !void {
|
|||
/// thread for final main thread setup requirements.
|
||||
pub fn finalizeSurfaceInit(self: *const OpenGL, surface: *apprt.Surface) !void {
|
||||
_ = self;
|
||||
_ = surface;
|
||||
|
||||
switch (apprt.runtime) {
|
||||
apprt.win32 => {
|
||||
// Release the WGL context from the main thread so the
|
||||
// renderer thread can acquire it.
|
||||
surface.releaseMainThreadContext();
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
/// Callback called by renderer.Thread when it begins.
|
||||
pub fn threadEnter(self: *const OpenGL, surface: *apprt.Surface) !void {
|
||||
_ = self;
|
||||
_ = surface;
|
||||
|
||||
switch (apprt.runtime) {
|
||||
else => @compileError("unsupported app runtime for OpenGL"),
|
||||
|
||||
apprt.gtk => {
|
||||
// GTK doesn't support threaded OpenGL operations as far as I can
|
||||
// tell, so we use the renderer thread to setup all the state
|
||||
// but then do the actual draws and texture syncs and all that
|
||||
// on the main thread. As such, we don't do anything here.
|
||||
},
|
||||
|
||||
apprt.embedded => {
|
||||
// TODO(mitchellh): this does nothing today to allow libghostty
|
||||
// to compile for OpenGL targets but libghostty is strictly
|
||||
// broken for rendering on this platforms.
|
||||
},
|
||||
if (apprt.runtime == apprt.win32) {
|
||||
// Win32: make the WGL context current on this thread and
|
||||
// reload GL function pointers.
|
||||
surface.makeContextCurrent();
|
||||
try prepareContext(null);
|
||||
}
|
||||
// GTK and embedded don't need thread-specific GL setup.
|
||||
}
|
||||
|
||||
/// Callback called by renderer.Thread when it exits.
|
||||
pub fn threadExit(self: *const OpenGL) void {
|
||||
_ = self;
|
||||
|
||||
switch (apprt.runtime) {
|
||||
else => @compileError("unsupported app runtime for OpenGL"),
|
||||
|
||||
apprt.gtk => {
|
||||
// We don't need to do any unloading for GTK because we may
|
||||
// be sharing the global bindings with other windows.
|
||||
},
|
||||
|
||||
apprt.embedded => {
|
||||
// TODO: see threadEnter
|
||||
},
|
||||
if (apprt.runtime == apprt.win32) {
|
||||
// Release the WGL context from this thread.
|
||||
apprt.win32.Surface.releaseContext();
|
||||
}
|
||||
// GTK and embedded don't need thread-specific GL cleanup.
|
||||
}
|
||||
|
||||
pub fn displayRealized(self: *const OpenGL) void {
|
||||
|
|
|
|||
|
|
@ -513,6 +513,12 @@ fn drawFrame(self: *Thread, now: bool) void {
|
|||
} else {
|
||||
self.renderer.drawFrame(false) catch |err|
|
||||
log.warn("error drawing err={}", .{err});
|
||||
|
||||
// On Win32, we need to explicitly swap buffers after rendering
|
||||
// since there's no toolkit managing the GL context for us.
|
||||
if (comptime @hasDecl(apprt.runtime.Surface, "swapBuffers")) {
|
||||
self.surface.swapBuffers();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue