1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
|
const std = @import("std");
const build_options = @import("build_options");
const Allocator = std.mem.Allocator;
const gdk = @import("gdk");
const Config = @import("../../config.zig").Config;
const input = @import("../../input.zig");
const key = @import("key.zig");
const ApprtWindow = @import("class/window.zig").Window;
const GlobalShortcuts = @import("class/global_shortcuts.zig").GlobalShortcuts;
pub const noop = @import("winproto/noop.zig");
pub const x11 = @import("winproto/x11.zig");
pub const wayland = @import("winproto/wayland.zig");
pub const Protocol = enum {
none,
wayland,
x11,
};
/// App-state for the underlying windowing protocol. There should be one
/// instance of this struct per application.
pub const App = union(Protocol) {
none: noop.App,
wayland: if (build_options.wayland) wayland.App else noop.App,
x11: if (build_options.x11) x11.App else noop.App,
pub fn init(
alloc: Allocator,
gdk_display: *gdk.Display,
app_id: [:0]const u8,
config: *const Config,
) !App {
inline for (@typeInfo(App).@"union".fields) |field| {
if (try field.type.init(
alloc,
gdk_display,
app_id,
config,
)) |v| {
return @unionInit(App, field.name, v);
}
}
return .{ .none = .{} };
}
pub fn deinit(self: *App) void {
switch (self.*) {
inline else => |*v| v.deinit(),
}
}
pub fn eventMods(
self: *App,
device: ?*gdk.Device,
gtk_mods: gdk.ModifierType,
) input.Mods {
return switch (self.*) {
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),
}
}
/// Bind all global keybinds through a mechanism native to the
/// windowing protocol. Returns false if there is none; the caller
/// should then fall back to the XDG desktop portal.
pub fn bindGlobalShortcuts(
self: *App,
shortcuts: *GlobalShortcuts,
config: *const Config,
) bool {
return switch (self.*) {
inline else => |*v| v.bindGlobalShortcuts(shortcuts, config),
};
}
/// Unbind all global keybinds bound via bindGlobalShortcuts.
pub fn clearGlobalShortcuts(self: *App) void {
switch (self.*) {
inline else => |*v| v.clearGlobalShortcuts(),
}
}
};
/// Per-Window state for the underlying windowing protocol.
///
/// In Wayland, the terminology used is "Surface" and for it, this is
/// really "Surface"-specific state. But Ghostty uses the term "Surface"
/// heavily to mean something completely different, so we use "Window" here
/// to better match what it generally maps to in the Ghostty codebase.
pub const Window = union(Protocol) {
none: noop.Window,
wayland: if (build_options.wayland) wayland.Window else noop.Window,
x11: if (build_options.x11) x11.Window else noop.Window,
pub fn init(
alloc: Allocator,
app: *App,
apprt_window: *ApprtWindow,
) !Window {
return switch (app.*) {
inline else => |*v, tag| {
inline for (@typeInfo(Window).@"union".fields) |field| {
if (comptime std.mem.eql(
u8,
field.name,
@tagName(tag),
)) return @unionInit(
Window,
field.name,
try field.type.init(
alloc,
v,
apprt_window,
),
);
}
},
};
}
pub fn deinit(self: *Window) void {
switch (self.*) {
inline else => |*v| v.deinit(),
}
}
pub fn resizeEvent(self: *Window) !void {
switch (self.*) {
inline else => |*v| try v.resizeEvent(),
}
}
pub fn syncAppearance(self: *Window) !void {
switch (self.*) {
inline else => |*v| try v.syncAppearance(),
}
}
pub fn clientSideDecorationEnabled(self: Window) bool {
return switch (self) {
inline else => |v| v.clientSideDecorationEnabled(),
};
}
pub fn addSubprocessEnv(self: *Window, env: *std.process.Environ.Map) !void {
switch (self.*) {
inline else => |*v| try v.addSubprocessEnv(env),
}
}
pub fn setUrgent(self: *Window, urgent: bool) !void {
switch (self.*) {
inline else => |*v| try v.setUrgent(urgent),
}
}
};
|