mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
gtk: implement quick terminal (#6027)
This commit is contained in:
@ -31,6 +31,7 @@
|
||||
glib,
|
||||
glslang,
|
||||
gtk4,
|
||||
gtk4-layer-shell,
|
||||
gobject-introspection,
|
||||
libadwaita,
|
||||
blueprint-compiler,
|
||||
@ -88,6 +89,7 @@
|
||||
|
||||
libadwaita
|
||||
gtk4
|
||||
gtk4-layer-shell
|
||||
glib
|
||||
gobject-introspection
|
||||
wayland
|
||||
@ -167,6 +169,7 @@ in
|
||||
blueprint-compiler
|
||||
libadwaita
|
||||
gtk4
|
||||
gtk4-layer-shell
|
||||
glib
|
||||
gobject-introspection
|
||||
wayland
|
||||
|
@ -13,6 +13,7 @@
|
||||
libGL,
|
||||
glib,
|
||||
gtk4,
|
||||
gtk4-layer-shell,
|
||||
gobject-introspection,
|
||||
libadwaita,
|
||||
blueprint-compiler,
|
||||
@ -118,6 +119,7 @@ in
|
||||
libXrandr
|
||||
]
|
||||
++ lib.optionals enableWayland [
|
||||
gtk4-layer-shell
|
||||
wayland
|
||||
];
|
||||
|
||||
|
@ -73,6 +73,8 @@ parts:
|
||||
- blueprint-compiler
|
||||
- libgtk-4-dev
|
||||
- libadwaita-1-dev
|
||||
# TODO: Add when the Snap is updated to Ubuntu 24.10+
|
||||
# - gtk4-layer-shell
|
||||
- libxml2-utils
|
||||
- git
|
||||
- patchelf
|
||||
|
@ -70,6 +70,10 @@ config_errors_window: ?*ConfigErrorsWindow = null,
|
||||
/// The clipboard confirmation window, if it is currently open.
|
||||
clipboard_confirmation_window: ?*ClipboardConfirmationWindow = null,
|
||||
|
||||
/// The window containing the quick terminal.
|
||||
/// Null when never initialized.
|
||||
quick_terminal: ?*Window = null,
|
||||
|
||||
/// This is set to false when the main loop should exit.
|
||||
running: bool = true,
|
||||
|
||||
@ -497,10 +501,10 @@ pub fn performAction(
|
||||
.toggle_window_decorations => self.toggleWindowDecorations(target),
|
||||
.quit_timer => self.quitTimer(value),
|
||||
.prompt_title => try self.promptTitle(target),
|
||||
.toggle_quick_terminal => return try self.toggleQuickTerminal(),
|
||||
|
||||
// Unimplemented
|
||||
.close_all_windows,
|
||||
.toggle_quick_terminal,
|
||||
.toggle_visibility,
|
||||
.cell_size,
|
||||
.secure_input,
|
||||
@ -764,6 +768,33 @@ fn toggleWindowDecorations(
|
||||
}
|
||||
}
|
||||
|
||||
fn toggleQuickTerminal(self: *App) !bool {
|
||||
if (self.quick_terminal) |qt| {
|
||||
qt.toggleVisibility();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!self.winproto.supportsQuickTerminal()) {
|
||||
log.err("quick terminal not supported on current platform", .{});
|
||||
return false;
|
||||
}
|
||||
|
||||
const qt = Window.create(self.core_app.alloc, self) catch |err| {
|
||||
log.err("failed to initialize quick terminal={}", .{err});
|
||||
return true;
|
||||
};
|
||||
self.quick_terminal = qt;
|
||||
|
||||
// The setup has to happen *before* the window-specific winproto is
|
||||
// initialized, so we need to initialize it through the app winproto
|
||||
try self.winproto.initQuickTerminal(qt);
|
||||
|
||||
// Finalize creating the quick terminal
|
||||
try qt.newTab(null);
|
||||
qt.present();
|
||||
return true;
|
||||
}
|
||||
|
||||
fn quitTimer(self: *App, mode: apprt.action.QuitTimer) void {
|
||||
switch (mode) {
|
||||
.start => self.startQuitTimer(),
|
||||
@ -1372,6 +1403,9 @@ fn newWindow(self: *App, parent_: ?*CoreSurface) !void {
|
||||
|
||||
// Add our initial tab
|
||||
try window.newTab(parent_);
|
||||
|
||||
// Show the new window
|
||||
window.present();
|
||||
}
|
||||
|
||||
fn quit(self: *App) void {
|
||||
|
@ -193,10 +193,6 @@ pub fn addTab(self: *TabView, tab: *Tab, title: [:0]const u8) void {
|
||||
}
|
||||
|
||||
pub fn closeTab(self: *TabView, tab: *Tab) void {
|
||||
// Save a pointer to the GTK window in case we need it later. It may be
|
||||
// impossible to access later due to how resources are cleaned up.
|
||||
const window: *gtk.Window = @ptrCast(@alignCast(self.window.window));
|
||||
|
||||
// closeTab always expects to close unconditionally so we mark this
|
||||
// as true so that the close_page call below doesn't request
|
||||
// confirmation.
|
||||
@ -225,7 +221,7 @@ pub fn closeTab(self: *TabView, tab: *Tab) void {
|
||||
box.as(gobject.Object).unref();
|
||||
}
|
||||
|
||||
window.destroy();
|
||||
self.window.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -234,7 +230,9 @@ pub fn createWindow(currentWindow: *Window) !*Window {
|
||||
const app = currentWindow.app;
|
||||
|
||||
// Create a new window
|
||||
return Window.create(alloc, app);
|
||||
const window = try Window.create(alloc, app);
|
||||
window.present();
|
||||
return window;
|
||||
}
|
||||
|
||||
fn adwPageAttached(_: *adw.TabView, page: *adw.TabPage, _: c_int, self: *TabView) callconv(.C) void {
|
||||
|
@ -79,6 +79,8 @@ pub const DerivedConfig = struct {
|
||||
gtk_wide_tabs: bool,
|
||||
gtk_toolbar_style: configpkg.Config.GtkToolbarStyle,
|
||||
|
||||
quick_terminal_position: configpkg.Config.QuickTerminalPosition,
|
||||
|
||||
maximize: bool,
|
||||
fullscreen: bool,
|
||||
window_decoration: configpkg.Config.WindowDecoration,
|
||||
@ -94,6 +96,8 @@ pub const DerivedConfig = struct {
|
||||
.gtk_wide_tabs = config.@"gtk-wide-tabs",
|
||||
.gtk_toolbar_style = config.@"gtk-toolbar-style",
|
||||
|
||||
.quick_terminal_position = config.@"quick-terminal-position",
|
||||
|
||||
.maximize = config.maximize,
|
||||
.fullscreen = config.fullscreen,
|
||||
.window_decoration = config.@"window-decoration",
|
||||
@ -364,9 +368,16 @@ pub fn init(self: *Window, app: *App) !void {
|
||||
|
||||
// If we are in fullscreen mode, new windows start fullscreen.
|
||||
if (self.config.fullscreen) c.gtk_window_fullscreen(self.window);
|
||||
}
|
||||
|
||||
// Show the window
|
||||
c.gtk_widget_show(gtk_widget);
|
||||
pub fn present(self: *Window) void {
|
||||
const window: *gtk.Window = @ptrCast(self.window);
|
||||
window.present();
|
||||
}
|
||||
|
||||
pub fn toggleVisibility(self: *Window) void {
|
||||
const window: *gtk.Widget = @ptrCast(self.window);
|
||||
window.setVisible(@intFromBool(window.isVisible() == 0));
|
||||
}
|
||||
|
||||
pub fn updateConfig(
|
||||
@ -408,6 +419,9 @@ pub fn syncAppearance(self: *Window) !void {
|
||||
// Never display the header bar when CSDs are disabled.
|
||||
if (!csd_enabled) break :visible false;
|
||||
|
||||
// Never display the header bar as a quick terminal.
|
||||
if (self.app.quick_terminal == self) break :visible false;
|
||||
|
||||
// Unconditionally disable the header bar when fullscreened.
|
||||
if (self.config.fullscreen) break :visible false;
|
||||
|
||||
@ -458,11 +472,11 @@ pub fn syncAppearance(self: *Window) !void {
|
||||
log.warn("failed to sync winproto appearance error={}", .{err});
|
||||
};
|
||||
|
||||
toggleCssClass(
|
||||
@ptrCast(self.window),
|
||||
"background",
|
||||
self.config.background_opacity >= 1,
|
||||
);
|
||||
if (self.app.quick_terminal == self) {
|
||||
self.winproto.syncQuickTerminal() catch |err| {
|
||||
log.warn("failed to sync quick terminal appearance error={}", .{err});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn toggleCssClass(
|
||||
@ -780,11 +794,23 @@ fn adwTabOverviewFocusTimer(
|
||||
return 0;
|
||||
}
|
||||
|
||||
pub fn close(self: *Window) void {
|
||||
const window: *gtk.Window = @ptrCast(self.window);
|
||||
|
||||
// Unset the quick terminal on the app level
|
||||
if (self.app.quick_terminal == self) self.app.quick_terminal = null;
|
||||
|
||||
window.destroy();
|
||||
}
|
||||
|
||||
fn gtkCloseRequest(v: *c.GtkWindow, ud: ?*anyopaque) callconv(.C) bool {
|
||||
_ = v;
|
||||
log.debug("window close request", .{});
|
||||
const self = userdataSelf(ud.?);
|
||||
|
||||
// This path should never occur, but this is here as a safety measure.
|
||||
if (self.app.quick_terminal == self) return true;
|
||||
|
||||
// If none of our surfaces need confirmation, we can just exit.
|
||||
for (self.app.core_app.surfaces.items) |surface| {
|
||||
if (surface.container.window()) |window| {
|
||||
@ -792,7 +818,7 @@ fn gtkCloseRequest(v: *c.GtkWindow, ud: ?*anyopaque) callconv(.C) bool {
|
||||
surface.core_surface.needsConfirmQuit()) break;
|
||||
}
|
||||
} else {
|
||||
c.gtk_window_destroy(self.window);
|
||||
self.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -836,7 +862,7 @@ fn gtkCloseConfirmation(
|
||||
c.gtk_window_destroy(@ptrCast(alert));
|
||||
if (response == c.GTK_RESPONSE_YES) {
|
||||
const self = userdataSelf(ud.?);
|
||||
c.gtk_window_destroy(self.window);
|
||||
self.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -934,7 +960,7 @@ fn gtkActionClose(
|
||||
_: ?*glib.Variant,
|
||||
self: *Window,
|
||||
) callconv(.C) void {
|
||||
c.gtk_window_destroy(self.window);
|
||||
self.close();
|
||||
}
|
||||
|
||||
fn gtkActionNewWindow(
|
||||
|
@ -15,6 +15,7 @@ pub const c = @cImport({
|
||||
@cInclude("X11/XKBlib.h");
|
||||
}
|
||||
if (build_options.wayland) {
|
||||
if (build_options.layer_shell) @cInclude("gtk4-layer-shell/gtk4-layer-shell.h");
|
||||
@cInclude("gdk/wayland/gdkwayland.h");
|
||||
}
|
||||
|
||||
|
@ -59,6 +59,23 @@ pub const App = union(Protocol) {
|
||||
inline else => |*v| v.eventMods(device, gtk_mods),
|
||||
} orelse key.translateMods(gtk_mods);
|
||||
}
|
||||
|
||||
pub fn supportsQuickTerminal(self: App) bool {
|
||||
return switch (self) {
|
||||
inline else => |v| v.supportsQuickTerminal(),
|
||||
};
|
||||
}
|
||||
|
||||
/// Set up necessary support for the quick terminal that must occur
|
||||
/// *before* the window-level winproto object is created.
|
||||
///
|
||||
/// Only has an effect on the Wayland backend, where the gtk4-layer-shell
|
||||
/// library is initialized.
|
||||
pub fn initQuickTerminal(self: *App, apprt_window: *ApprtWindow) !void {
|
||||
switch (self.*) {
|
||||
inline else => |*v| try v.initQuickTerminal(apprt_window),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// Per-Window state for the underlying windowing protocol.
|
||||
@ -116,6 +133,12 @@ pub const Window = union(Protocol) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn syncQuickTerminal(self: *Window) !void {
|
||||
switch (self.*) {
|
||||
inline else => |*v| try v.syncQuickTerminal(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clientSideDecorationEnabled(self: Window) bool {
|
||||
return switch (self) {
|
||||
inline else => |v| v.clientSideDecorationEnabled(),
|
||||
|
@ -29,6 +29,11 @@ pub const App = struct {
|
||||
) ?input.Mods {
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn supportsQuickTerminal(_: App) bool {
|
||||
return false;
|
||||
}
|
||||
pub fn initQuickTerminal(_: *App, _: *ApprtWindow) !void {}
|
||||
};
|
||||
|
||||
pub const Window = struct {
|
||||
@ -54,6 +59,8 @@ pub const Window = struct {
|
||||
|
||||
pub fn syncAppearance(_: *Window) !void {}
|
||||
|
||||
pub fn syncQuickTerminal(_: *Window) !void {}
|
||||
|
||||
/// This returns true if CSD is enabled for this window. This
|
||||
/// should be the actual present state of the window, not the
|
||||
/// desired state.
|
||||
|
@ -1,7 +1,10 @@
|
||||
//! Wayland protocol implementation for the Ghostty GTK apprt.
|
||||
const std = @import("std");
|
||||
const wayland = @import("wayland");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const build_options = @import("build_options");
|
||||
const wayland = @import("wayland");
|
||||
|
||||
const c = @import("../c.zig").c;
|
||||
const Config = @import("../../../config.zig").Config;
|
||||
const input = @import("../../../input.zig");
|
||||
@ -84,6 +87,20 @@ pub const App = struct {
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn supportsQuickTerminal(_: App) bool {
|
||||
if (comptime !build_options.layer_shell) return false;
|
||||
|
||||
return c.gtk_layer_is_supported() != 0;
|
||||
}
|
||||
|
||||
pub fn initQuickTerminal(_: *App, apprt_window: *ApprtWindow) !void {
|
||||
if (comptime !build_options.layer_shell) unreachable;
|
||||
|
||||
c.gtk_layer_init_for_window(apprt_window.window);
|
||||
c.gtk_layer_set_layer(apprt_window.window, c.GTK_LAYER_SHELL_LAYER_TOP);
|
||||
c.gtk_layer_set_keyboard_mode(apprt_window.window, c.GTK_LAYER_SHELL_KEYBOARD_MODE_ON_DEMAND);
|
||||
}
|
||||
|
||||
fn registryListener(
|
||||
registry: *wl.Registry,
|
||||
event: wl.Registry.Event,
|
||||
@ -156,7 +173,7 @@ pub const App = struct {
|
||||
|
||||
/// Per-window (wl_surface) state for the Wayland protocol.
|
||||
pub const Window = struct {
|
||||
config: *const ApprtWindow.DerivedConfig,
|
||||
apprt_window: *ApprtWindow,
|
||||
|
||||
/// The Wayland surface for this window.
|
||||
surface: *wl.Surface,
|
||||
@ -210,7 +227,7 @@ pub const Window = struct {
|
||||
};
|
||||
|
||||
return .{
|
||||
.config = &apprt_window.config,
|
||||
.apprt_window = apprt_window,
|
||||
.surface = wl_surface,
|
||||
.app_context = app.context,
|
||||
.blur_token = null,
|
||||
@ -255,7 +272,7 @@ pub const Window = struct {
|
||||
/// Update the blur state of the window.
|
||||
fn syncBlur(self: *Window) !void {
|
||||
const manager = self.app_context.kde_blur_manager orelse return;
|
||||
const blur = self.config.background_blur;
|
||||
const blur = self.apprt_window.config.background_blur;
|
||||
|
||||
if (self.blur_token) |tok| {
|
||||
// Only release token when transitioning from blurred -> not blurred
|
||||
@ -283,11 +300,51 @@ pub const Window = struct {
|
||||
}
|
||||
|
||||
fn getDecorationMode(self: Window) org.KdeKwinServerDecorationManager.Mode {
|
||||
return switch (self.config.window_decoration) {
|
||||
return switch (self.apprt_window.config.window_decoration) {
|
||||
.auto => self.app_context.default_deco_mode orelse .Client,
|
||||
.client => .Client,
|
||||
.server => .Server,
|
||||
.none => .None,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn syncQuickTerminal(self: *Window) !void {
|
||||
if (comptime !build_options.layer_shell) return;
|
||||
|
||||
const window = self.apprt_window.window;
|
||||
|
||||
const anchored_edge: ?LayerShellEdge = switch (self.apprt_window.config.quick_terminal_position) {
|
||||
.left => .left,
|
||||
.right => .right,
|
||||
.top => .top,
|
||||
.bottom => .bottom,
|
||||
.center => null,
|
||||
};
|
||||
|
||||
for (std.meta.tags(LayerShellEdge)) |edge| {
|
||||
if (anchored_edge) |anchored| {
|
||||
if (edge == anchored) {
|
||||
c.gtk_layer_set_margin(window, @intFromEnum(edge), 0);
|
||||
c.gtk_layer_set_anchor(window, @intFromEnum(edge), @intFromBool(true));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Arbitrary margin - could be made customizable?
|
||||
c.gtk_layer_set_margin(window, @intFromEnum(edge), 20);
|
||||
c.gtk_layer_set_anchor(window, @intFromEnum(edge), @intFromBool(false));
|
||||
}
|
||||
|
||||
switch (self.apprt_window.config.quick_terminal_position) {
|
||||
.top, .bottom, .center => c.gtk_window_set_default_size(window, 800, 400),
|
||||
.left, .right => c.gtk_window_set_default_size(window, 400, 800),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const LayerShellEdge = enum(c_uint) {
|
||||
left = c.GTK_LAYER_SHELL_EDGE_LEFT,
|
||||
right = c.GTK_LAYER_SHELL_EDGE_RIGHT,
|
||||
top = c.GTK_LAYER_SHELL_EDGE_TOP,
|
||||
bottom = c.GTK_LAYER_SHELL_EDGE_BOTTOM,
|
||||
};
|
||||
|
@ -148,6 +148,12 @@ pub const App = struct {
|
||||
|
||||
return mods;
|
||||
}
|
||||
|
||||
pub fn supportsQuickTerminal(_: App) bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn initQuickTerminal(_: *App, _: *ApprtWindow) !void {}
|
||||
};
|
||||
|
||||
pub const Window = struct {
|
||||
@ -222,6 +228,8 @@ pub const Window = struct {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn syncQuickTerminal(_: *Window) !void {}
|
||||
|
||||
pub fn clientSideDecorationEnabled(self: Window) bool {
|
||||
return switch (self.config.window_decoration) {
|
||||
.auto, .client => true,
|
||||
|
@ -34,6 +34,7 @@ font_backend: font.Backend = .freetype,
|
||||
/// Feature flags
|
||||
x11: bool = false,
|
||||
wayland: bool = false,
|
||||
layer_shell: bool = false,
|
||||
sentry: bool = true,
|
||||
wasm_shared: bool = true,
|
||||
|
||||
@ -109,7 +110,6 @@ pub fn init(b: *std.Build) !Config {
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Comptime Interfaces
|
||||
|
||||
config.font_backend = b.option(
|
||||
font.Backend,
|
||||
"font-backend",
|
||||
@ -163,6 +163,12 @@ pub fn init(b: *std.Build) !Config {
|
||||
"Enables linking against X11 libraries when using the GTK rendering backend.",
|
||||
) orelse gtk_targets.x11;
|
||||
|
||||
config.layer_shell = b.option(
|
||||
bool,
|
||||
"gtk-layer-shell",
|
||||
"Enables linking against the gtk4-layer-shell library for quick terminal support. Requires Wayland.",
|
||||
) orelse gtk_targets.layer_shell;
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Ghostty Exe Properties
|
||||
|
||||
@ -392,6 +398,7 @@ pub fn addOptions(self: *const Config, step: *std.Build.Step.Options) !void {
|
||||
step.addOption(bool, "flatpak", self.flatpak);
|
||||
step.addOption(bool, "x11", self.x11);
|
||||
step.addOption(bool, "wayland", self.wayland);
|
||||
step.addOption(bool, "layer_shell", self.layer_shell);
|
||||
step.addOption(bool, "sentry", self.sentry);
|
||||
step.addOption(apprt.Runtime, "app_runtime", self.app_runtime);
|
||||
step.addOption(font.Backend, "font_backend", self.font_backend);
|
||||
|
@ -460,12 +460,8 @@ pub fn add(
|
||||
|
||||
if (self.config.wayland) {
|
||||
const scanner = Scanner.create(b.dependency("zig_wayland", .{}), .{
|
||||
// We shouldn't be using getPath but we need to for now
|
||||
// https://codeberg.org/ifreund/zig-wayland/issues/66
|
||||
.wayland_xml = b.dependency("wayland", .{})
|
||||
.path("protocol/wayland.xml"),
|
||||
.wayland_protocols = b.dependency("wayland_protocols", .{})
|
||||
.path(""),
|
||||
.wayland_xml = b.dependency("wayland", .{}).path("protocol/wayland.xml"),
|
||||
.wayland_protocols = b.dependency("wayland_protocols", .{}).path(""),
|
||||
});
|
||||
|
||||
const wayland = b.createModule(.{ .root_source_file = scanner.result });
|
||||
@ -485,6 +481,8 @@ pub fn add(
|
||||
|
||||
step.root_module.addImport("wayland", wayland);
|
||||
step.root_module.addImport("gdk_wayland", gobject.module("gdkwayland4"));
|
||||
|
||||
if (self.config.layer_shell) step.linkSystemLibrary2("gtk4-layer-shell", dynamic_link_opts);
|
||||
step.linkSystemLibrary2("wayland-client", dynamic_link_opts);
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,8 @@ RUN DEBIAN_FRONTEND="noninteractive" apt-get -qq update && \
|
||||
# Ghostty Dependencies
|
||||
libadwaita-1-dev \
|
||||
libgtk-4-dev && \
|
||||
# TODO: Add when this is updated to Debian 13++
|
||||
# gtk4-layer-shell
|
||||
# Clean up for better caching
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
@ -3,6 +3,7 @@ const std = @import("std");
|
||||
pub const Targets = packed struct {
|
||||
x11: bool = false,
|
||||
wayland: bool = false,
|
||||
layer_shell: bool = false,
|
||||
};
|
||||
|
||||
/// Returns the targets that GTK4 was compiled with.
|
||||
@ -17,8 +18,24 @@ pub fn targets(b: *std.Build) Targets {
|
||||
.Ignore,
|
||||
) catch return .{};
|
||||
|
||||
const x11 = std.mem.indexOf(u8, output, "x11") != null;
|
||||
const wayland = std.mem.indexOf(u8, output, "wayland") != null;
|
||||
|
||||
const layer_shell = layer_shell: {
|
||||
if (!wayland) break :layer_shell false;
|
||||
|
||||
_ = b.runAllowFail(
|
||||
&.{ "pkg-config", "--exists", "gtk4-layer-shell-0" },
|
||||
&code,
|
||||
.Ignore,
|
||||
) catch break :layer_shell false;
|
||||
|
||||
break :layer_shell true;
|
||||
};
|
||||
|
||||
return .{
|
||||
.x11 = std.mem.indexOf(u8, output, "x11") != null,
|
||||
.wayland = std.mem.indexOf(u8, output, "wayland") != null,
|
||||
.x11 = x11,
|
||||
.wayland = wayland,
|
||||
.layer_shell = layer_shell,
|
||||
};
|
||||
}
|
||||
|
Reference in New Issue
Block a user