diff --git a/src/apprt/gtk/App.zig b/src/apprt/gtk/App.zig index bf1e80fef..fba7bf89e 100644 --- a/src/apprt/gtk/App.zig +++ b/src/apprt/gtk/App.zig @@ -192,9 +192,7 @@ pub fn newWindow(self: *App, parent_: ?*CoreSurface) !void { // // The allocation is owned by the GtkWindow created. It will be // freed when the window is closed. - var window = try alloc.create(Window); - errdefer alloc.destroy(window); - try window.init(self); + var window = try Window.create(alloc, self); // Add our initial tab try window.newTab(parent_); diff --git a/src/apprt/gtk/Surface.zig b/src/apprt/gtk/Surface.zig index b4a97e38a..398f2bf85 100644 --- a/src/apprt/gtk/Surface.zig +++ b/src/apprt/gtk/Surface.zig @@ -158,6 +158,7 @@ pub fn init(self: *Surface, app: *App, opts: Options) !void { // GL events _ = c.g_signal_connect_data(opts.gl_area, "realize", c.G_CALLBACK(>kRealize), self, null, c.G_CONNECT_DEFAULT); + _ = c.g_signal_connect_data(opts.gl_area, "unrealize", c.G_CALLBACK(>kUnrealize), self, null, c.G_CONNECT_DEFAULT); _ = c.g_signal_connect_data(opts.gl_area, "destroy", c.G_CALLBACK(>kDestroy), self, null, c.G_CONNECT_DEFAULT); _ = c.g_signal_connect_data(opts.gl_area, "render", c.G_CALLBACK(>kRender), self, null, c.G_CONNECT_DEFAULT); _ = c.g_signal_connect_data(opts.gl_area, "resize", c.G_CALLBACK(>kResize), self, null, c.G_CONNECT_DEFAULT); @@ -177,6 +178,16 @@ pub fn init(self: *Surface, app: *App, opts: Options) !void { } fn realize(self: *Surface) !void { + // If this surface has already been realized, then we don't need to + // reinitialize. This can happen if a surface is moved from one GDK surface + // to another (i.e. a tab is pulled out into a window). + if (self.realized) { + // If we have no OpenGL state though, we do need to reinitialize. + // We allow the renderer to figure that out + try self.core_surface.renderer.displayRealize(); + return; + } + // Add ourselves to the list of surfaces on the app. try self.app.core_app.addSurface(self); errdefer self.app.core_app.deleteSurface(self); @@ -479,6 +490,15 @@ fn gtkRealize(area: *c.GtkGLArea, ud: ?*anyopaque) callconv(.C) void { }; } +/// This is called when the underlying OpenGL resources must be released. +/// This is usually due to the OpenGL area changing GDK surfaces. +fn gtkUnrealize(area: *c.GtkGLArea, ud: ?*anyopaque) callconv(.C) void { + _ = area; + + const self = userdataSelf(ud.?); + self.core_surface.renderer.displayUnrealized(); +} + /// render signal fn gtkRender(area: *c.GtkGLArea, ctx: *c.GdkGLContext, ud: ?*anyopaque) callconv(.C) c.gboolean { _ = area; diff --git a/src/apprt/gtk/Window.zig b/src/apprt/gtk/Window.zig index 24a82bfe3..99d2e14d8 100644 --- a/src/apprt/gtk/Window.zig +++ b/src/apprt/gtk/Window.zig @@ -3,6 +3,7 @@ const Window = @This(); const std = @import("std"); const builtin = @import("builtin"); +const Allocator = std.mem.Allocator; const assert = std.debug.assert; const configpkg = @import("../../config.zig"); const font = @import("../../font/main.zig"); @@ -29,6 +30,20 @@ notebook: *c.GtkNotebook, /// pointer to this because GTK can use it at any time. icon_search_dir: ?[:0]const u8 = null, +pub fn create(alloc: Allocator, app: *App) !*Window { + // Allocate a fixed pointer for our window. We try to minimize + // allocations but windows and other GUI requirements are so minimal + // compared to the steady-state terminal operation so we use heap + // allocation for this. + // + // The allocation is owned by the GtkWindow created. It will be + // freed when the window is closed. + var window = try alloc.create(Window); + errdefer alloc.destroy(window); + try window.init(app); + return window; +} + pub fn init(self: *Window, app: *App) !void { // Set up our own state self.* = .{