gtk: remove c.zig (#6792)

It has been done.
vm-tests
Jeffrey C. Ollie 2025-03-18 09:59:23 -05:00 committed by GitHub
commit ee78a3d345
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 672 additions and 678 deletions

View File

@ -697,7 +697,10 @@ jobs:
msgcmp "$old_pot" po/com.mitchellh.ghostty.pot --use-untranslated msgcmp "$old_pot" po/com.mitchellh.ghostty.pot --use-untranslated
# Compare all other POs to current POT # Compare all other POs to current POT
for f in po/*.po; do msgcmp "$f" po/com.mitchellh.ghostty.pot; done for f in po/*.po; do
# Ignore untranslated entries
msgcmp --use-untranslated "$f" po/com.mitchellh.ghostty.pot;
done
test-pkg-linux: test-pkg-linux:
strategy: strategy:

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: com.mitchellh.ghostty\n" "Project-Id-Version: com.mitchellh.ghostty\n"
"Report-Msgid-Bugs-To: m@mitchellh.com\n" "Report-Msgid-Bugs-To: m@mitchellh.com\n"
"POT-Creation-Date: 2025-03-06 20:10+0100\n" "POT-Creation-Date: 2025-03-18 11:48+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -34,6 +34,26 @@ msgstr ""
msgid "OK" msgid "OK"
msgstr "" msgstr ""
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:5
msgid "Configuration Errors"
msgstr ""
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:6
msgid ""
"One or more configuration errors were found. Please review the errors below, "
"and either reload your configuration or ignore these errors."
msgstr ""
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:9
msgid "Ignore"
msgstr ""
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:10
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:97
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:95
msgid "Reload Configuration"
msgstr ""
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:6 #: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:6
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:6 #: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:6
msgid "Copy" msgid "Copy"
@ -123,17 +143,12 @@ msgstr ""
msgid "Open Configuration" msgid "Open Configuration"
msgstr "" msgstr ""
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:97
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:95
msgid "Reload Configuration"
msgstr ""
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:85 #: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:85
msgid "Terminal Inspector" msgid "Terminal Inspector"
msgstr "" msgstr ""
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:102 #: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:102
#: src/apprt/gtk/Window.zig:933 #: src/apprt/gtk/Window.zig:958
msgid "About Ghostty" msgid "About Ghostty"
msgstr "" msgstr ""
@ -178,27 +193,31 @@ msgid ""
"commands may be executed." "commands may be executed."
msgstr "" msgstr ""
#: src/apprt/gtk/Window.zig:199 #: src/apprt/gtk/Window.zig:200
msgid "Main Menu" msgid "Main Menu"
msgstr "" msgstr ""
#: src/apprt/gtk/Window.zig:219 #: src/apprt/gtk/Window.zig:221
msgid "View Open Tabs" msgid "View Open Tabs"
msgstr "" msgstr ""
#: src/apprt/gtk/Window.zig:265 #: src/apprt/gtk/Window.zig:295
msgid "" msgid ""
"⚠️ You're running a debug build of Ghostty! Performance will be degraded." "⚠️ You're running a debug build of Ghostty! Performance will be degraded."
msgstr "" msgstr ""
#: src/apprt/gtk/Window.zig:681 #: src/apprt/gtk/Window.zig:725
msgid "Reloaded the configuration" msgid "Reloaded the configuration"
msgstr "" msgstr ""
#: src/apprt/gtk/Window.zig:914 #: src/apprt/gtk/Window.zig:939
msgid "Ghostty Developers" msgid "Ghostty Developers"
msgstr "" msgstr ""
#: src/apprt/gtk/inspector.zig:144
msgid "Ghostty: Terminal Inspector"
msgstr ""
#: src/apprt/gtk/CloseDialog.zig:47 #: src/apprt/gtk/CloseDialog.zig:47
msgid "Close" msgid "Close"
msgstr "" msgstr ""
@ -235,6 +254,6 @@ msgstr ""
msgid "The currently running process in this split will be terminated." msgid "The currently running process in this split will be terminated."
msgstr "" msgstr ""
#: src/apprt/gtk/Surface.zig:1128 #: src/apprt/gtk/Surface.zig:1242
msgid "Copied to clipboard" msgid "Copied to clipboard"
msgstr "" msgstr ""

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: com.mitchellh.ghostty\n" "Project-Id-Version: com.mitchellh.ghostty\n"
"Report-Msgid-Bugs-To: m@mitchellh.com\n" "Report-Msgid-Bugs-To: m@mitchellh.com\n"
"POT-Creation-Date: 2025-03-06 20:10+0100\n" "POT-Creation-Date: 2025-03-18 11:48+0100\n"
"PO-Revision-Date: 2025-03-06 14:57+0100\n" "PO-Revision-Date: 2025-03-06 14:57+0100\n"
"Last-Translator: Robin <r@rpfaeffle.com>\n" "Last-Translator: Robin <r@rpfaeffle.com>\n"
"Language-Team: German <translation-team-de@lists.sourceforge.net>\n" "Language-Team: German <translation-team-de@lists.sourceforge.net>\n"
@ -35,6 +35,26 @@ msgstr "Abbrechen"
msgid "OK" msgid "OK"
msgstr "OK" msgstr "OK"
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:5
msgid "Configuration Errors"
msgstr ""
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:6
msgid ""
"One or more configuration errors were found. Please review the errors below, "
"and either reload your configuration or ignore these errors."
msgstr ""
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:9
msgid "Ignore"
msgstr ""
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:10
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:97
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:95
msgid "Reload Configuration"
msgstr "Konfiguration neu laden"
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:6 #: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:6
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:6 #: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:6
msgid "Copy" msgid "Copy"
@ -124,17 +144,12 @@ msgstr "Konfiguration"
msgid "Open Configuration" msgid "Open Configuration"
msgstr "Konfiguration öffnen" msgstr "Konfiguration öffnen"
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:97
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:95
msgid "Reload Configuration"
msgstr "Konfiguration neu laden"
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:85 #: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:85
msgid "Terminal Inspector" msgid "Terminal Inspector"
msgstr "Terminalinspektor" msgstr "Terminalinspektor"
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:102 #: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:102
#: src/apprt/gtk/Window.zig:933 #: src/apprt/gtk/Window.zig:958
msgid "About Ghostty" msgid "About Ghostty"
msgstr "Über Ghostty" msgstr "Über Ghostty"
@ -185,29 +200,33 @@ msgstr ""
"Diesen Text in das Terminal einzufügen könnte möglicherweise gefährlich " "Diesen Text in das Terminal einzufügen könnte möglicherweise gefährlich "
"sein. Es scheint, dass Anweisungen ausgeführt werden könnten." "sein. Es scheint, dass Anweisungen ausgeführt werden könnten."
#: src/apprt/gtk/Window.zig:199 #: src/apprt/gtk/Window.zig:200
msgid "Main Menu" msgid "Main Menu"
msgstr "Hauptmenü" msgstr "Hauptmenü"
#: src/apprt/gtk/Window.zig:219 #: src/apprt/gtk/Window.zig:221
msgid "View Open Tabs" msgid "View Open Tabs"
msgstr "Offene Tabs einblenden" msgstr "Offene Tabs einblenden"
#: src/apprt/gtk/Window.zig:265 #: src/apprt/gtk/Window.zig:295
msgid "" msgid ""
"⚠️ You're running a debug build of Ghostty! Performance will be degraded." "⚠️ You're running a debug build of Ghostty! Performance will be degraded."
msgstr "" msgstr ""
"⚠️ Du verwendest einen Debug Build von Ghostty! Die Leistung wird reduziert " "⚠️ Du verwendest einen Debug Build von Ghostty! Die Leistung wird reduziert "
"sein." "sein."
#: src/apprt/gtk/Window.zig:681 #: src/apprt/gtk/Window.zig:725
msgid "Reloaded the configuration" msgid "Reloaded the configuration"
msgstr "Konfiguration wurde neu geladen" msgstr "Konfiguration wurde neu geladen"
#: src/apprt/gtk/Window.zig:914 #: src/apprt/gtk/Window.zig:939
msgid "Ghostty Developers" msgid "Ghostty Developers"
msgstr "Ghostty-Entwickler" msgstr "Ghostty-Entwickler"
#: src/apprt/gtk/inspector.zig:144
msgid "Ghostty: Terminal Inspector"
msgstr ""
#: src/apprt/gtk/CloseDialog.zig:47 #: src/apprt/gtk/CloseDialog.zig:47
msgid "Close" msgid "Close"
msgstr "Schließen" msgstr "Schließen"
@ -244,6 +263,6 @@ msgstr "Alle Terminalsitzungen in diesem Tab werden beendet."
msgid "The currently running process in this split will be terminated." msgid "The currently running process in this split will be terminated."
msgstr "Der aktuell laufende Prozess in diesem geteilten Fenster wird beendet." msgstr "Der aktuell laufende Prozess in diesem geteilten Fenster wird beendet."
#: src/apprt/gtk/Surface.zig:1128 #: src/apprt/gtk/Surface.zig:1242
msgid "Copied to clipboard" msgid "Copied to clipboard"
msgstr "In die Zwischenablage kopiert" msgstr "In die Zwischenablage kopiert"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: com.mitchellh.ghostty\n" "Project-Id-Version: com.mitchellh.ghostty\n"
"Report-Msgid-Bugs-To: m@mitchellh.com\n" "Report-Msgid-Bugs-To: m@mitchellh.com\n"
"POT-Creation-Date: 2025-03-06 20:10+0100\n" "POT-Creation-Date: 2025-03-18 11:48+0100\n"
"PO-Revision-Date: 2025-02-27 09:16+0100\n" "PO-Revision-Date: 2025-02-27 09:16+0100\n"
"Last-Translator: Leah <hi@pluie.me>\n" "Last-Translator: Leah <hi@pluie.me>\n"
"Language-Team: Chinese (simplified) <i18n-zh@googlegroups.com>\n" "Language-Team: Chinese (simplified) <i18n-zh@googlegroups.com>\n"
@ -34,6 +34,28 @@ msgstr "取消"
msgid "OK" msgid "OK"
msgstr "确认" msgstr "确认"
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:5
msgid "Configuration Errors"
msgstr "设置错误"
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:6
msgid ""
"One or more configuration errors were found. Please review the errors below, "
"and either reload your configuration or ignore these errors."
msgstr ""
"加载设置时发现了以下错误。请仔细阅读错误信息,"
"并选择忽略或重新加载设置文件。"
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:9
msgid "Ignore"
msgstr "忽略"
#: src/apprt/gtk/ui/1.5/config-errors-dialog.blp:10
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:97
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:95
msgid "Reload Configuration"
msgstr "重新加载设置"
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:6 #: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:6
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:6 #: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:6
msgid "Copy" msgid "Copy"
@ -123,17 +145,12 @@ msgstr "设置"
msgid "Open Configuration" msgid "Open Configuration"
msgstr "打开设置文件" msgstr "打开设置文件"
#: src/apprt/gtk/ui/1.0/menu-surface-context_menu.blp:97
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:95
msgid "Reload Configuration"
msgstr "重新加载设置"
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:85 #: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:85
msgid "Terminal Inspector" msgid "Terminal Inspector"
msgstr "终端检视器" msgstr "终端检视器"
#: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:102 #: src/apprt/gtk/ui/1.0/menu-window-titlebar_menu.blp:102
#: src/apprt/gtk/Window.zig:933 #: src/apprt/gtk/Window.zig:958
msgid "About Ghostty" msgid "About Ghostty"
msgstr "关于 Ghostty" msgstr "关于 Ghostty"
@ -178,27 +195,31 @@ msgid ""
"commands may be executed." "commands may be executed."
msgstr "将以下内容粘贴至终端内将可能执行有害命令。" msgstr "将以下内容粘贴至终端内将可能执行有害命令。"
#: src/apprt/gtk/Window.zig:199 #: src/apprt/gtk/Window.zig:200
msgid "Main Menu" msgid "Main Menu"
msgstr "主菜单" msgstr "主菜单"
#: src/apprt/gtk/Window.zig:219 #: src/apprt/gtk/Window.zig:221
msgid "View Open Tabs" msgid "View Open Tabs"
msgstr "浏览标签页" msgstr "浏览标签页"
#: src/apprt/gtk/Window.zig:265 #: src/apprt/gtk/Window.zig:295
msgid "" msgid ""
"⚠️ You're running a debug build of Ghostty! Performance will be degraded." "⚠️ You're running a debug build of Ghostty! Performance will be degraded."
msgstr "⚠️ Ghostty 正在以调试模式运行!性能将大打折扣。" msgstr "⚠️ Ghostty 正在以调试模式运行!性能将大打折扣。"
#: src/apprt/gtk/Window.zig:681 #: src/apprt/gtk/Window.zig:725
msgid "Reloaded the configuration" msgid "Reloaded the configuration"
msgstr "已重新加载设置" msgstr "已重新加载设置"
#: src/apprt/gtk/Window.zig:914 #: src/apprt/gtk/Window.zig:939
msgid "Ghostty Developers" msgid "Ghostty Developers"
msgstr "Ghostty 开发团队" msgstr "Ghostty 开发团队"
#: src/apprt/gtk/inspector.zig:144
msgid "Ghostty: Terminal Inspector"
msgstr "Ghostty 终端检视器"
#: src/apprt/gtk/CloseDialog.zig:47 #: src/apprt/gtk/CloseDialog.zig:47
msgid "Close" msgid "Close"
msgstr "关闭" msgstr "关闭"
@ -235,6 +256,6 @@ msgstr "标签页内所有运行中的进程将被终止。"
msgid "The currently running process in this split will be terminated." msgid "The currently running process in this split will be terminated."
msgstr "分屏内正在运行中的进程将被终止。" msgstr "分屏内正在运行中的进程将被终止。"
#: src/apprt/gtk/Surface.zig:1128 #: src/apprt/gtk/Surface.zig:1242
msgid "Copied to clipboard" msgid "Copied to clipboard"
msgstr "已复制至剪切板" msgstr "已复制至剪切板"

View File

@ -36,7 +36,7 @@ const CoreSurface = @import("../../Surface.zig");
const cgroup = @import("cgroup.zig"); const cgroup = @import("cgroup.zig");
const Surface = @import("Surface.zig"); const Surface = @import("Surface.zig");
const Window = @import("Window.zig"); const Window = @import("Window.zig");
const ConfigErrorsWindow = @import("ConfigErrorsWindow.zig"); const ConfigErrorsDialog = @import("ConfigErrorsDialog.zig");
const ClipboardConfirmationWindow = @import("ClipboardConfirmationWindow.zig"); const ClipboardConfirmationWindow = @import("ClipboardConfirmationWindow.zig");
const CloseDialog = @import("CloseDialog.zig"); const CloseDialog = @import("CloseDialog.zig");
const Split = @import("Split.zig"); const Split = @import("Split.zig");
@ -71,9 +71,6 @@ single_instance: bool,
/// The "none" cursor. We use one that is shared across the entire app. /// The "none" cursor. We use one that is shared across the entire app.
cursor_none: ?*gdk.Cursor, cursor_none: ?*gdk.Cursor,
/// The configuration errors window, if it is currently open.
config_errors_window: ?*ConfigErrorsWindow = null,
/// The clipboard confirmation window, if it is currently open. /// The clipboard confirmation window, if it is currently open.
clipboard_confirmation_window: ?*ClipboardConfirmationWindow = null, clipboard_confirmation_window: ?*ClipboardConfirmationWindow = null,
@ -956,16 +953,22 @@ fn configChange(
log.warn("error cloning configuration err={}", .{err}); log.warn("error cloning configuration err={}", .{err});
} }
self.syncConfigChanges() catch |err| {
log.warn("error handling configuration changes err={}", .{err});
};
// App changes needs to show a toast that our configuration // App changes needs to show a toast that our configuration
// has reloaded. // has reloaded.
if (self.core_app.focusedSurface()) |core_surface| { const window = window: {
const surface = core_surface.rt_surface; if (self.core_app.focusedSurface()) |core_surface| {
if (surface.container.window()) |window| window.onConfigReloaded(); const surface = core_surface.rt_surface;
} if (surface.container.window()) |window| {
window.onConfigReloaded();
break :window window;
}
}
break :window null;
};
self.syncConfigChanges(window) catch |err| {
log.warn("error handling configuration changes err={}", .{err});
};
}, },
} }
} }
@ -1001,8 +1004,8 @@ pub fn reloadConfig(
} }
/// Call this anytime the configuration changes. /// Call this anytime the configuration changes.
fn syncConfigChanges(self: *App) !void { fn syncConfigChanges(self: *App, window: ?*Window) !void {
try self.updateConfigErrors(); ConfigErrorsDialog.maybePresent(self, window);
try self.syncActionAccelerators(); try self.syncActionAccelerators();
// Load our runtime and custom CSS. If this fails then our window is just stuck // Load our runtime and custom CSS. If this fails then our window is just stuck
@ -1018,23 +1021,6 @@ fn syncConfigChanges(self: *App) !void {
}; };
} }
/// This should be called whenever the configuration changes to update
/// the state of our config errors window. This will show the window if
/// there are new configuration errors and hide the window if the errors
/// are resolved.
fn updateConfigErrors(self: *App) !void {
if (!self.config._diagnostics.empty()) {
if (self.config_errors_window == null) {
try ConfigErrorsWindow.create(self);
assert(self.config_errors_window != null);
}
}
if (self.config_errors_window) |window| {
window.update();
}
}
fn syncActionAccelerators(self: *App) !void { fn syncActionAccelerators(self: *App) !void {
try self.syncActionAccelerator("app.quit", .{ .quit = {} }); try self.syncActionAccelerator("app.quit", .{ .quit = {} });
try self.syncActionAccelerator("app.open-config", .{ .open_config = {} }); try self.syncActionAccelerator("app.open-config", .{ .open_config = {} });
@ -1309,13 +1295,6 @@ pub fn run(self: *App) !void {
// Setup our actions // Setup our actions
self.initActions(); self.initActions();
// On startup, we want to check for configuration errors right away
// so we can show our error window. We also need to setup other initial
// state.
self.syncConfigChanges() catch |err| {
log.warn("error handling configuration changes err={}", .{err});
};
while (self.running) { while (self.running) {
_ = glib.MainContext.iteration(self.ctx, 1); _ = glib.MainContext.iteration(self.ctx, 1);

View File

@ -12,7 +12,6 @@ const gio = @import("gio");
const apprt = @import("../../apprt.zig"); const apprt = @import("../../apprt.zig");
const CoreSurface = @import("../../Surface.zig"); const CoreSurface = @import("../../Surface.zig");
const App = @import("App.zig"); const App = @import("App.zig");
const View = @import("View.zig");
const Builder = @import("Builder.zig"); const Builder = @import("Builder.zig");
const adwaita = @import("adwaita.zig"); const adwaita = @import("adwaita.zig");

View File

@ -0,0 +1,77 @@
/// Configuration errors window.
const ConfigErrorsDialog = @This();
const std = @import("std");
const Allocator = std.mem.Allocator;
const gobject = @import("gobject");
const gio = @import("gio");
const gtk = @import("gtk");
const adw = @import("adw");
const build_config = @import("../../build_config.zig");
const configpkg = @import("../../config.zig");
const Config = configpkg.Config;
const App = @import("App.zig");
const Window = @import("Window.zig");
const Builder = @import("Builder.zig");
const adwaita = @import("adwaita.zig");
const log = std.log.scoped(.gtk);
const DialogType = if (adwaita.supportsDialogs()) adw.AlertDialog else adw.MessageDialog;
builder: Builder,
dialog: *DialogType,
error_message: *gtk.TextBuffer,
pub fn maybePresent(app: *App, window: ?*Window) void {
if (app.config._diagnostics.empty()) return;
var builder = switch (DialogType) {
adw.AlertDialog => Builder.init("config-errors-dialog", 1, 5, .blp),
adw.MessageDialog => Builder.init("config-errors-dialog", 1, 2, .ui),
else => unreachable,
};
defer builder.deinit();
const dialog = builder.getObject(DialogType, "config_errors_dialog").?;
const error_message = builder.getObject(gtk.TextBuffer, "error_message").?;
var msg_buf: [4095:0]u8 = undefined;
var fbs = std.io.fixedBufferStream(&msg_buf);
for (app.config._diagnostics.items()) |diag| {
fbs.reset();
diag.write(fbs.writer()) catch |err| {
log.warn(
"error writing diagnostic to buffer err={}",
.{err},
);
continue;
};
error_message.insertAtCursor(&msg_buf, @intCast(fbs.pos));
error_message.insertAtCursor("\n", 1);
}
_ = DialogType.signals.response.connect(dialog, *App, onResponse, app, .{});
const parent: ?*gtk.Widget = if (window) |w| @ptrCast(w.window) else null;
switch (DialogType) {
adw.AlertDialog => dialog.as(adw.Dialog).present(parent),
adw.MessageDialog => dialog.as(gtk.Window).present(),
else => unreachable,
}
}
fn onResponse(_: *DialogType, response: [*:0]const u8, app: *App) callconv(.C) void {
if (std.mem.orderZ(u8, response, "reload") == .eq) {
app.reloadConfig(.app, .{}) catch |err| {
log.warn("error reloading config error={}", .{err});
return;
};
}
}

View File

@ -1,218 +0,0 @@
/// Configuration errors window.
const ConfigErrors = @This();
const std = @import("std");
const Allocator = std.mem.Allocator;
const build_config = @import("../../build_config.zig");
const configpkg = @import("../../config.zig");
const Config = configpkg.Config;
const App = @import("App.zig");
const View = @import("View.zig");
const c = @import("c.zig").c;
const log = std.log.scoped(.gtk);
app: *App,
window: *c.GtkWindow,
view: PrimaryView,
pub fn create(app: *App) !void {
if (app.config_errors_window != null) return error.InvalidOperation;
const alloc = app.core_app.alloc;
const self = try alloc.create(ConfigErrors);
errdefer alloc.destroy(self);
try self.init(app);
app.config_errors_window = self;
}
pub fn update(self: *ConfigErrors) void {
if (self.app.config._diagnostics.empty()) {
c.gtk_window_destroy(@ptrCast(self.window));
return;
}
self.view.update(&self.app.config);
_ = c.gtk_window_present(self.window);
_ = c.gtk_widget_grab_focus(@ptrCast(self.window));
}
/// Not public because this should be called by the GTK lifecycle.
fn destroy(self: *ConfigErrors) void {
const alloc = self.app.core_app.alloc;
self.app.config_errors_window = null;
alloc.destroy(self);
}
fn init(self: *ConfigErrors, app: *App) !void {
// Create the window
const window = c.gtk_window_new();
const gtk_window: *c.GtkWindow = @ptrCast(window);
errdefer c.gtk_window_destroy(gtk_window);
c.gtk_window_set_title(gtk_window, "Configuration Errors");
c.gtk_window_set_default_size(gtk_window, 600, 275);
c.gtk_window_set_resizable(gtk_window, 0);
c.gtk_window_set_icon_name(gtk_window, build_config.bundle_id);
c.gtk_widget_add_css_class(@ptrCast(@alignCast(gtk_window)), "window");
c.gtk_widget_add_css_class(@ptrCast(@alignCast(gtk_window)), "config-errors-window");
_ = c.g_signal_connect_data(window, "destroy", c.G_CALLBACK(&gtkDestroy), self, null, c.G_CONNECT_DEFAULT);
// Set some state
self.* = .{
.app = app,
.window = gtk_window,
.view = undefined,
};
// Show the window
const view = try PrimaryView.init(self);
self.view = view;
c.gtk_window_set_child(@ptrCast(window), view.root);
c.gtk_widget_show(window);
}
fn gtkDestroy(_: *c.GtkWidget, ud: ?*anyopaque) callconv(.C) void {
const self = userdataSelf(ud.?);
self.destroy();
}
fn userdataSelf(ud: *anyopaque) *ConfigErrors {
return @ptrCast(@alignCast(ud));
}
const PrimaryView = struct {
root: *c.GtkWidget,
text: *c.GtkTextView,
pub fn init(root: *ConfigErrors) !PrimaryView {
// All our widgets
const label = c.gtk_label_new(
"One or more configuration errors were found while loading " ++
"the configuration. Please review the errors below and reload " ++
"your configuration or ignore the erroneous lines.",
);
const buf = contentsBuffer(&root.app.config);
defer c.g_object_unref(buf);
const buttons = try ButtonsView.init(root);
const text_scroll = c.gtk_scrolled_window_new();
errdefer c.g_object_unref(text_scroll);
const text = c.gtk_text_view_new_with_buffer(buf);
errdefer c.g_object_unref(text);
c.gtk_scrolled_window_set_child(@ptrCast(text_scroll), text);
// Create our view
const view = try View.init(&.{
.{ .name = "label", .widget = label },
.{ .name = "text", .widget = text_scroll },
.{ .name = "buttons", .widget = buttons.root },
}, &vfl);
errdefer view.deinit();
// We can do additional settings once the layout is setup
c.gtk_label_set_wrap(@ptrCast(label), 1);
c.gtk_text_view_set_editable(@ptrCast(text), 0);
c.gtk_text_view_set_cursor_visible(@ptrCast(text), 0);
c.gtk_text_view_set_top_margin(@ptrCast(text), 8);
c.gtk_text_view_set_bottom_margin(@ptrCast(text), 8);
c.gtk_text_view_set_left_margin(@ptrCast(text), 8);
c.gtk_text_view_set_right_margin(@ptrCast(text), 8);
return .{ .root = view.root, .text = @ptrCast(text) };
}
pub fn update(self: *PrimaryView, config: *const Config) void {
const buf = contentsBuffer(config);
defer c.g_object_unref(buf);
c.gtk_text_view_set_buffer(@ptrCast(self.text), buf);
}
/// Returns the GtkTextBuffer for the config errors that we want to show.
fn contentsBuffer(config: *const Config) *c.GtkTextBuffer {
const buf = c.gtk_text_buffer_new(null);
errdefer c.g_object_unref(buf);
var msg_buf: [4096]u8 = undefined;
var fbs = std.io.fixedBufferStream(&msg_buf);
for (config._diagnostics.items()) |diag| {
fbs.reset();
diag.write(fbs.writer()) catch |err| {
log.warn(
"error writing diagnostic to buffer err={}",
.{err},
);
continue;
};
const msg = fbs.getWritten();
c.gtk_text_buffer_insert_at_cursor(buf, msg.ptr, @intCast(msg.len));
c.gtk_text_buffer_insert_at_cursor(buf, "\n", -1);
}
return buf;
}
const vfl = [_][*:0]const u8{
"H:|-8-[label]-8-|",
"H:|[text]|",
"H:|[buttons]|",
"V:|[label(<=80)][text(>=100)]-[buttons]-|",
};
};
const ButtonsView = struct {
root: *c.GtkWidget,
pub fn init(root: *ConfigErrors) !ButtonsView {
const ignore_button = c.gtk_button_new_with_label("Ignore");
errdefer c.g_object_unref(ignore_button);
const reload_button = c.gtk_button_new_with_label("Reload Configuration");
errdefer c.g_object_unref(reload_button);
// Create our view
const view = try View.init(&.{
.{ .name = "ignore", .widget = ignore_button },
.{ .name = "reload", .widget = reload_button },
}, &vfl);
// Signals
_ = c.g_signal_connect_data(
ignore_button,
"clicked",
c.G_CALLBACK(&gtkIgnoreClick),
root,
null,
c.G_CONNECT_DEFAULT,
);
_ = c.g_signal_connect_data(
reload_button,
"clicked",
c.G_CALLBACK(&gtkReloadClick),
root,
null,
c.G_CONNECT_DEFAULT,
);
return .{ .root = view.root };
}
fn gtkIgnoreClick(_: *c.GtkWidget, ud: ?*anyopaque) callconv(.C) void {
const self: *ConfigErrors = @ptrCast(@alignCast(ud));
c.gtk_window_destroy(@ptrCast(self.window));
}
fn gtkReloadClick(_: *c.GtkWidget, ud: ?*anyopaque) callconv(.C) void {
const self: *ConfigErrors = @ptrCast(@alignCast(ud));
self.app.reloadConfig(.app, .{}) catch |err| {
log.warn("error reloading config error={}", .{err});
return;
};
}
const vfl = [_][*:0]const u8{
"H:[ignore]-8-[reload]-8-|",
};
};

View File

@ -3,10 +3,12 @@ const ImguiWidget = @This();
const std = @import("std"); const std = @import("std");
const assert = std.debug.assert; const assert = std.debug.assert;
const gdk = @import("gdk");
const gtk = @import("gtk");
const cimgui = @import("cimgui"); const cimgui = @import("cimgui");
const c = @import("c.zig").c;
const key = @import("key.zig");
const gl = @import("opengl"); const gl = @import("opengl");
const key = @import("key.zig");
const input = @import("../../input.zig"); const input = @import("../../input.zig");
const log = std.log.scoped(.gtk_imgui_widget); const log = std.log.scoped(.gtk_imgui_widget);
@ -16,8 +18,8 @@ render_callback: ?*const fn (?*anyopaque) void = null,
render_userdata: ?*anyopaque = null, render_userdata: ?*anyopaque = null,
/// Our OpenGL widget /// Our OpenGL widget
gl_area: *c.GtkGLArea, gl_area: *gtk.GLArea,
im_context: *c.GtkIMContext, im_context: *gtk.IMContext,
/// ImGui Context /// ImGui Context
ig_ctx: *cimgui.c.ImGuiContext, ig_ctx: *cimgui.c.ImGuiContext,
@ -36,65 +38,145 @@ pub fn init(self: *ImguiWidget) !void {
io.BackendPlatformName = "ghostty_gtk"; io.BackendPlatformName = "ghostty_gtk";
// Our OpenGL area for drawing // Our OpenGL area for drawing
const gl_area = c.gtk_gl_area_new(); const gl_area = gtk.GLArea.new();
c.gtk_gl_area_set_auto_render(@ptrCast(gl_area), 1); gl_area.setAutoRender(@intFromBool(true));
// The GL area has to be focusable so that it can receive events // The GL area has to be focusable so that it can receive events
c.gtk_widget_set_focusable(@ptrCast(gl_area), 1); gl_area.as(gtk.Widget).setFocusable(@intFromBool(true));
c.gtk_widget_set_focus_on_click(@ptrCast(gl_area), 1); gl_area.as(gtk.Widget).setFocusOnClick(@intFromBool(true));
// Clicks // Clicks
const gesture_click = c.gtk_gesture_click_new(); const gesture_click = gtk.GestureClick.new();
errdefer c.g_object_unref(gesture_click); errdefer gesture_click.unref();
c.gtk_gesture_single_set_button(@ptrCast(gesture_click), 0); gesture_click.as(gtk.GestureSingle).setButton(0);
c.gtk_widget_add_controller(@ptrCast(gl_area), @ptrCast(gesture_click)); gl_area.as(gtk.Widget).addController(gesture_click.as(gtk.EventController));
// Mouse movement // Mouse movement
const ec_motion = c.gtk_event_controller_motion_new(); const ec_motion = gtk.EventControllerMotion.new();
errdefer c.g_object_unref(ec_motion); errdefer ec_motion.unref();
c.gtk_widget_add_controller(@ptrCast(gl_area), ec_motion); gl_area.as(gtk.Widget).addController(ec_motion.as(gtk.EventController));
// Scroll events // Scroll events
const ec_scroll = c.gtk_event_controller_scroll_new( const ec_scroll = gtk.EventControllerScroll.new(.flags_both_axes);
c.GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES | errdefer ec_scroll.unref();
c.GTK_EVENT_CONTROLLER_SCROLL_DISCRETE, gl_area.as(gtk.Widget).addController(ec_scroll.as(gtk.EventController));
);
errdefer c.g_object_unref(ec_scroll);
c.gtk_widget_add_controller(@ptrCast(gl_area), ec_scroll);
// Focus controller will tell us about focus enter/exit events // Focus controller will tell us about focus enter/exit events
const ec_focus = c.gtk_event_controller_focus_new(); const ec_focus = gtk.EventControllerFocus.new();
errdefer c.g_object_unref(ec_focus); errdefer ec_focus.unref();
c.gtk_widget_add_controller(@ptrCast(gl_area), ec_focus); gl_area.as(gtk.Widget).addController(ec_focus.as(gtk.EventController));
// Key event controller will tell us about raw keypress events. // Key event controller will tell us about raw keypress events.
const ec_key = c.gtk_event_controller_key_new(); const ec_key = gtk.EventControllerKey.new();
errdefer c.g_object_unref(ec_key); errdefer ec_key.unref();
c.gtk_widget_add_controller(@ptrCast(gl_area), ec_key); gl_area.as(gtk.Widget).addController(ec_key.as(gtk.EventController));
errdefer c.gtk_widget_remove_controller(@ptrCast(gl_area), ec_key); errdefer gl_area.as(gtk.Widget).removeController(ec_key.as(gtk.EventController));
// The input method context that we use to translate key events into // The input method context that we use to translate key events into
// characters. This doesn't have an event key controller attached because // characters. This doesn't have an event key controller attached because
// we call it manually from our own key controller. // we call it manually from our own key controller.
const im_context = c.gtk_im_multicontext_new(); const im_context = gtk.IMMulticontext.new();
errdefer c.g_object_unref(im_context); errdefer im_context.unref();
// Signals // Signals
_ = c.g_signal_connect_data(gl_area, "destroy", c.G_CALLBACK(&gtkDestroy), self, null, c.G_CONNECT_DEFAULT); _ = gtk.Widget.signals.realize.connect(
_ = c.g_signal_connect_data(gl_area, "realize", c.G_CALLBACK(&gtkRealize), self, null, c.G_CONNECT_DEFAULT); gl_area,
_ = c.g_signal_connect_data(gl_area, "unrealize", c.G_CALLBACK(&gtkUnrealize), self, null, c.G_CONNECT_DEFAULT); *ImguiWidget,
_ = c.g_signal_connect_data(gl_area, "render", c.G_CALLBACK(&gtkRender), self, null, c.G_CONNECT_DEFAULT); gtkRealize,
_ = c.g_signal_connect_data(gl_area, "resize", c.G_CALLBACK(&gtkResize), self, null, c.G_CONNECT_DEFAULT); self,
.{},
_ = c.g_signal_connect_data(ec_focus, "enter", c.G_CALLBACK(&gtkFocusEnter), self, null, c.G_CONNECT_DEFAULT); );
_ = c.g_signal_connect_data(ec_focus, "leave", c.G_CALLBACK(&gtkFocusLeave), self, null, c.G_CONNECT_DEFAULT); _ = gtk.Widget.signals.unrealize.connect(
_ = c.g_signal_connect_data(ec_key, "key-pressed", c.G_CALLBACK(&gtkKeyPressed), self, null, c.G_CONNECT_DEFAULT); gl_area,
_ = c.g_signal_connect_data(ec_key, "key-released", c.G_CALLBACK(&gtkKeyReleased), self, null, c.G_CONNECT_DEFAULT); *ImguiWidget,
_ = c.g_signal_connect_data(ec_motion, "motion", c.G_CALLBACK(&gtkMouseMotion), self, null, c.G_CONNECT_DEFAULT); gtkUnrealize,
_ = c.g_signal_connect_data(ec_scroll, "scroll", c.G_CALLBACK(&gtkMouseScroll), self, null, c.G_CONNECT_DEFAULT); self,
_ = c.g_signal_connect_data(gesture_click, "pressed", c.G_CALLBACK(&gtkMouseDown), self, null, c.G_CONNECT_DEFAULT); .{},
_ = c.g_signal_connect_data(gesture_click, "released", c.G_CALLBACK(&gtkMouseUp), self, null, c.G_CONNECT_DEFAULT); );
_ = c.g_signal_connect_data(im_context, "commit", c.G_CALLBACK(&gtkInputCommit), self, null, c.G_CONNECT_DEFAULT); _ = gtk.Widget.signals.destroy.connect(
gl_area,
*ImguiWidget,
gtkDestroy,
self,
.{},
);
_ = gtk.GLArea.signals.render.connect(
gl_area,
*ImguiWidget,
gtkRender,
self,
.{},
);
_ = gtk.GLArea.signals.resize.connect(
gl_area,
*ImguiWidget,
gtkResize,
self,
.{},
);
_ = gtk.EventControllerKey.signals.key_pressed.connect(
ec_key,
*ImguiWidget,
gtkKeyPressed,
self,
.{},
);
_ = gtk.EventControllerKey.signals.key_released.connect(
ec_key,
*ImguiWidget,
gtkKeyReleased,
self,
.{},
);
_ = gtk.EventControllerFocus.signals.enter.connect(
ec_focus,
*ImguiWidget,
gtkFocusEnter,
self,
.{},
);
_ = gtk.EventControllerFocus.signals.leave.connect(
ec_focus,
*ImguiWidget,
gtkFocusLeave,
self,
.{},
);
_ = gtk.GestureClick.signals.pressed.connect(
gesture_click,
*ImguiWidget,
gtkMouseDown,
self,
.{},
);
_ = gtk.GestureClick.signals.released.connect(
gesture_click,
*ImguiWidget,
gtkMouseUp,
self,
.{},
);
_ = gtk.EventControllerMotion.signals.motion.connect(
ec_motion,
*ImguiWidget,
gtkMouseMotion,
self,
.{},
);
_ = gtk.EventControllerScroll.signals.scroll.connect(
ec_scroll,
*ImguiWidget,
gtkMouseScroll,
self,
.{},
);
_ = gtk.IMContext.signals.commit.connect(
im_context,
*ImguiWidget,
gtkInputCommit,
self,
.{},
);
self.* = .{ self.* = .{
.gl_area = @ptrCast(gl_area), .gl_area = @ptrCast(gl_area),
@ -113,7 +195,7 @@ pub fn deinit(self: *ImguiWidget) void {
/// This should be called anytime the underlying data for the UI changes /// This should be called anytime the underlying data for the UI changes
/// so that the UI can be refreshed. /// so that the UI can be refreshed.
pub fn queueRender(self: *const ImguiWidget) void { pub fn queueRender(self: *const ImguiWidget) void {
c.gtk_gl_area_queue_render(self.gl_area); self.gl_area.queueRender();
} }
/// Initialize the frame. Expects that the context is already current. /// Initialize the frame. Expects that the context is already current.
@ -130,7 +212,7 @@ fn newFrame(self: *ImguiWidget) !void {
self.instant = now; self.instant = now;
} }
fn translateMouseButton(button: c.guint) ?c_int { fn translateMouseButton(button: c_uint) ?c_int {
return switch (button) { return switch (button) {
1 => cimgui.c.ImGuiMouseButton_Left, 1 => cimgui.c.ImGuiMouseButton_Left,
2 => cimgui.c.ImGuiMouseButton_Middle, 2 => cimgui.c.ImGuiMouseButton_Middle,
@ -139,45 +221,39 @@ fn translateMouseButton(button: c.guint) ?c_int {
}; };
} }
fn gtkDestroy(v: *c.GtkWidget, ud: ?*anyopaque) callconv(.C) void { fn gtkDestroy(_: *gtk.GLArea, self: *ImguiWidget) callconv(.C) void {
_ = v;
log.debug("imgui widget destroy", .{}); log.debug("imgui widget destroy", .{});
const self: *ImguiWidget = @ptrCast(@alignCast(ud.?));
self.deinit(); self.deinit();
} }
fn gtkRealize(area: *c.GtkGLArea, ud: ?*anyopaque) callconv(.C) void { fn gtkRealize(area: *gtk.GLArea, self: *ImguiWidget) callconv(.C) void {
log.debug("gl surface realized", .{}); log.debug("gl surface realized", .{});
// We need to make the context current so we can call GL functions. // We need to make the context current so we can call GL functions.
c.gtk_gl_area_make_current(area); area.makeCurrent();
if (c.gtk_gl_area_get_error(area)) |err| { if (area.getError()) |err| {
log.err("surface failed to realize: {s}", .{err.*.message}); log.err("surface failed to realize: {s}", .{err.f_message orelse "(unknown)"});
return; return;
} }
// realize means that our OpenGL context is ready, so we can now // realize means that our OpenGL context is ready, so we can now
// initialize the ImgUI OpenGL backend for our context. // initialize the ImgUI OpenGL backend for our context.
const self: *ImguiWidget = @ptrCast(@alignCast(ud.?));
cimgui.c.igSetCurrentContext(self.ig_ctx); cimgui.c.igSetCurrentContext(self.ig_ctx);
_ = cimgui.ImGui_ImplOpenGL3_Init(null); _ = cimgui.ImGui_ImplOpenGL3_Init(null);
} }
fn gtkUnrealize(area: *c.GtkGLArea, ud: ?*anyopaque) callconv(.C) void { fn gtkUnrealize(area: *gtk.GLArea, self: *ImguiWidget) callconv(.C) void {
_ = area; _ = area;
log.debug("gl surface unrealized", .{}); log.debug("gl surface unrealized", .{});
const self: *ImguiWidget = @ptrCast(@alignCast(ud.?));
cimgui.c.igSetCurrentContext(self.ig_ctx); cimgui.c.igSetCurrentContext(self.ig_ctx);
cimgui.ImGui_ImplOpenGL3_Shutdown(); cimgui.ImGui_ImplOpenGL3_Shutdown();
} }
fn gtkResize(area: *c.GtkGLArea, width: c.gint, height: c.gint, ud: ?*anyopaque) callconv(.C) void { fn gtkResize(area: *gtk.GLArea, width: c_int, height: c_int, self: *ImguiWidget) callconv(.C) void {
const self: *ImguiWidget = @ptrCast(@alignCast(ud.?));
cimgui.c.igSetCurrentContext(self.ig_ctx); cimgui.c.igSetCurrentContext(self.ig_ctx);
const io: *cimgui.c.ImGuiIO = cimgui.c.igGetIO(); const io: *cimgui.c.ImGuiIO = cimgui.c.igGetIO();
const scale_factor = c.gtk_widget_get_scale_factor(@ptrCast(area)); const scale_factor = area.as(gtk.Widget).getScaleFactor();
log.debug("gl resize width={} height={} scale={}", .{ log.debug("gl resize width={} height={} scale={}", .{
width, width,
height, height,
@ -197,10 +273,7 @@ fn gtkResize(area: *c.GtkGLArea, width: c.gint, height: c.gint, ud: ?*anyopaque)
active_style.* = style.*; active_style.* = style.*;
} }
fn gtkRender(area: *c.GtkGLArea, ctx: *c.GdkGLContext, ud: ?*anyopaque) callconv(.C) c.gboolean { fn gtkRender(_: *gtk.GLArea, _: *gdk.GLContext, self: *ImguiWidget) callconv(.C) c_int {
_ = area;
_ = ctx;
const self: *ImguiWidget = @ptrCast(@alignCast(ud.?));
cimgui.c.igSetCurrentContext(self.ig_ctx); cimgui.c.igSetCurrentContext(self.ig_ctx);
// Setup our frame. We render twice because some ImGui behaviors // Setup our frame. We render twice because some ImGui behaviors
@ -230,17 +303,14 @@ fn gtkRender(area: *c.GtkGLArea, ctx: *c.GdkGLContext, ud: ?*anyopaque) callconv
} }
fn gtkMouseMotion( fn gtkMouseMotion(
_: *c.GtkEventControllerMotion, _: *gtk.EventControllerMotion,
x: c.gdouble, x: f64,
y: c.gdouble, y: f64,
ud: ?*anyopaque, self: *ImguiWidget,
) callconv(.C) void { ) callconv(.C) void {
const self: *ImguiWidget = @ptrCast(@alignCast(ud.?));
cimgui.c.igSetCurrentContext(self.ig_ctx); cimgui.c.igSetCurrentContext(self.ig_ctx);
const io: *cimgui.c.ImGuiIO = cimgui.c.igGetIO(); const io: *cimgui.c.ImGuiIO = cimgui.c.igGetIO();
const scale_factor: f64 = @floatFromInt(c.gtk_widget_get_scale_factor( const scale_factor: f64 = @floatFromInt(self.gl_area.as(gtk.Widget).getScaleFactor());
@ptrCast(self.gl_area),
));
cimgui.c.ImGuiIO_AddMousePosEvent( cimgui.c.ImGuiIO_AddMousePosEvent(
io, io,
@floatCast(x * scale_factor), @floatCast(x * scale_factor),
@ -250,48 +320,46 @@ fn gtkMouseMotion(
} }
fn gtkMouseDown( fn gtkMouseDown(
gesture: *c.GtkGestureClick, gesture: *gtk.GestureClick,
_: c.gint, _: c_int,
_: c.gdouble, _: f64,
_: c.gdouble, _: f64,
ud: ?*anyopaque, self: *ImguiWidget,
) callconv(.C) void { ) callconv(.C) void {
const self: *ImguiWidget = @ptrCast(@alignCast(ud.?));
self.queueRender(); self.queueRender();
cimgui.c.igSetCurrentContext(self.ig_ctx); cimgui.c.igSetCurrentContext(self.ig_ctx);
const io: *cimgui.c.ImGuiIO = cimgui.c.igGetIO(); const io: *cimgui.c.ImGuiIO = cimgui.c.igGetIO();
const gdk_button = c.gtk_gesture_single_get_current_button(@ptrCast(gesture));
const gdk_button = gesture.as(gtk.GestureSingle).getCurrentButton();
if (translateMouseButton(gdk_button)) |button| { if (translateMouseButton(gdk_button)) |button| {
cimgui.c.ImGuiIO_AddMouseButtonEvent(io, button, true); cimgui.c.ImGuiIO_AddMouseButtonEvent(io, button, true);
} }
} }
fn gtkMouseUp( fn gtkMouseUp(
gesture: *c.GtkGestureClick, gesture: *gtk.GestureClick,
_: c.gint, _: c_int,
_: c.gdouble, _: f64,
_: c.gdouble, _: f64,
ud: ?*anyopaque, self: *ImguiWidget,
) callconv(.C) void { ) callconv(.C) void {
const self: *ImguiWidget = @ptrCast(@alignCast(ud.?));
self.queueRender(); self.queueRender();
cimgui.c.igSetCurrentContext(self.ig_ctx); cimgui.c.igSetCurrentContext(self.ig_ctx);
const io: *cimgui.c.ImGuiIO = cimgui.c.igGetIO(); const io: *cimgui.c.ImGuiIO = cimgui.c.igGetIO();
const gdk_button = c.gtk_gesture_single_get_current_button(@ptrCast(gesture)); const gdk_button = gesture.as(gtk.GestureSingle).getCurrentButton();
if (translateMouseButton(gdk_button)) |button| { if (translateMouseButton(gdk_button)) |button| {
cimgui.c.ImGuiIO_AddMouseButtonEvent(io, button, false); cimgui.c.ImGuiIO_AddMouseButtonEvent(io, button, false);
} }
} }
fn gtkMouseScroll( fn gtkMouseScroll(
_: *c.GtkEventControllerScroll, _: *gtk.EventControllerScroll,
x: c.gdouble, x: f64,
y: c.gdouble, y: f64,
ud: ?*anyopaque, self: *ImguiWidget,
) callconv(.C) void { ) callconv(.C) c_int {
const self: *ImguiWidget = @ptrCast(@alignCast(ud.?));
self.queueRender(); self.queueRender();
cimgui.c.igSetCurrentContext(self.ig_ctx); cimgui.c.igSetCurrentContext(self.ig_ctx);
@ -301,10 +369,11 @@ fn gtkMouseScroll(
@floatCast(x), @floatCast(x),
@floatCast(-y), @floatCast(-y),
); );
return @intFromBool(true);
} }
fn gtkFocusEnter(_: *c.GtkEventControllerFocus, ud: ?*anyopaque) callconv(.C) void { fn gtkFocusEnter(_: *gtk.EventControllerFocus, self: *ImguiWidget) callconv(.C) void {
const self: *ImguiWidget = @ptrCast(@alignCast(ud.?));
self.queueRender(); self.queueRender();
cimgui.c.igSetCurrentContext(self.ig_ctx); cimgui.c.igSetCurrentContext(self.ig_ctx);
@ -312,8 +381,7 @@ fn gtkFocusEnter(_: *c.GtkEventControllerFocus, ud: ?*anyopaque) callconv(.C) vo
cimgui.c.ImGuiIO_AddFocusEvent(io, true); cimgui.c.ImGuiIO_AddFocusEvent(io, true);
} }
fn gtkFocusLeave(_: *c.GtkEventControllerFocus, ud: ?*anyopaque) callconv(.C) void { fn gtkFocusLeave(_: *gtk.EventControllerFocus, self: *ImguiWidget) callconv(.C) void {
const self: *ImguiWidget = @ptrCast(@alignCast(ud.?));
self.queueRender(); self.queueRender();
cimgui.c.igSetCurrentContext(self.ig_ctx); cimgui.c.igSetCurrentContext(self.ig_ctx);
@ -322,11 +390,10 @@ fn gtkFocusLeave(_: *c.GtkEventControllerFocus, ud: ?*anyopaque) callconv(.C) vo
} }
fn gtkInputCommit( fn gtkInputCommit(
_: *c.GtkIMContext, _: *gtk.IMMulticontext,
bytes: [*:0]u8, bytes: [*:0]u8,
ud: ?*anyopaque, self: *ImguiWidget,
) callconv(.C) void { ) callconv(.C) void {
const self: *ImguiWidget = @ptrCast(@alignCast(ud.?));
self.queueRender(); self.queueRender();
cimgui.c.igSetCurrentContext(self.ig_ctx); cimgui.c.igSetCurrentContext(self.ig_ctx);
@ -335,44 +402,53 @@ fn gtkInputCommit(
} }
fn gtkKeyPressed( fn gtkKeyPressed(
ec_key: *c.GtkEventControllerKey, ec_key: *gtk.EventControllerKey,
keyval: c.guint, keyval: c_uint,
keycode: c.guint, keycode: c_uint,
gtk_mods: c.GdkModifierType, gtk_mods: gdk.ModifierType,
ud: ?*anyopaque, self: *ImguiWidget,
) callconv(.C) c.gboolean { ) callconv(.C) c_int {
return if (keyEvent(.press, ec_key, keyval, keycode, gtk_mods, ud)) 1 else 0; return @intFromBool(self.keyEvent(
.press,
ec_key,
keyval,
keycode,
gtk_mods,
));
} }
fn gtkKeyReleased( fn gtkKeyReleased(
ec_key: *c.GtkEventControllerKey, ec_key: *gtk.EventControllerKey,
keyval: c.guint, keyval: c_uint,
keycode: c.guint, keycode: c_uint,
state: c.GdkModifierType, gtk_mods: gdk.ModifierType,
ud: ?*anyopaque, self: *ImguiWidget,
) callconv(.C) c.gboolean { ) callconv(.C) void {
return if (keyEvent(.release, ec_key, keyval, keycode, state, ud)) 1 else 0; _ = self.keyEvent(
.release,
ec_key,
keyval,
keycode,
gtk_mods,
);
} }
fn keyEvent( fn keyEvent(
self: *ImguiWidget,
action: input.Action, action: input.Action,
ec_key: *c.GtkEventControllerKey, ec_key: *gtk.EventControllerKey,
keyval: c.guint, keyval: c_uint,
keycode: c.guint, keycode: c_uint,
gtk_mods: c.GdkModifierType, gtk_mods: gdk.ModifierType,
ud: ?*anyopaque,
) bool { ) bool {
_ = keycode; _ = keycode;
const self: *ImguiWidget = @ptrCast(@alignCast(ud.?));
self.queueRender(); self.queueRender();
cimgui.c.igSetCurrentContext(self.ig_ctx); cimgui.c.igSetCurrentContext(self.ig_ctx);
const io: *cimgui.c.ImGuiIO = cimgui.c.igGetIO(); const io: *cimgui.c.ImGuiIO = cimgui.c.igGetIO();
// FIXME: when this file get converted to zig-gobject const mods = key.translateMods(gtk_mods);
// Translate the GTK mods and update the modifiers on every keypress
const mods = key.translateMods(@bitCast(gtk_mods));
cimgui.c.ImGuiIO_AddKeyEvent(io, cimgui.c.ImGuiKey_LeftShift, mods.shift); cimgui.c.ImGuiIO_AddKeyEvent(io, cimgui.c.ImGuiKey_LeftShift, mods.shift);
cimgui.c.ImGuiIO_AddKeyEvent(io, cimgui.c.ImGuiKey_LeftCtrl, mods.ctrl); cimgui.c.ImGuiIO_AddKeyEvent(io, cimgui.c.ImGuiKey_LeftCtrl, mods.ctrl);
cimgui.c.ImGuiIO_AddKeyEvent(io, cimgui.c.ImGuiKey_LeftAlt, mods.alt); cimgui.c.ImGuiIO_AddKeyEvent(io, cimgui.c.ImGuiKey_LeftAlt, mods.alt);
@ -386,9 +462,9 @@ fn keyEvent(
} }
// Try to process the event as text // Try to process the event as text
const event = c.gtk_event_controller_get_current_event(@ptrCast(ec_key)); if (ec_key.as(gtk.EventController).getCurrentEvent()) |event| {
if (event != null) _ = self.im_context.filterKeypress(event);
_ = c.gtk_im_context_filter_keypress(self.im_context, event); }
return true; return true;
} }

View File

@ -1,73 +0,0 @@
/// View helps with creating a view with a constraint layout by
/// managing all the boilerplate. The caller is responsible for
/// providing the widgets, their names, and the VFL code and gets
/// a root box as a result ready to be used.
const View = @This();
const std = @import("std");
const c = @import("c.zig").c;
const log = std.log.scoped(.gtk);
/// The box that contains all of the widgets.
root: *c.GtkWidget,
/// A single widget used in the view.
pub const Widget = struct {
/// The name of the widget used for the layout code. This is also
/// the name set for the widget for CSS styling.
name: [:0]const u8,
/// The widget itself.
widget: *c.GtkWidget,
};
/// Initialize a new constraint layout view with the given widgets
/// and VFL.
pub fn init(widgets: []const Widget, vfl: []const [*:0]const u8) !View {
// Box to store all our widgets
const box = c.gtk_box_new(c.GTK_ORIENTATION_VERTICAL, 0);
errdefer c.g_object_unref(box);
c.gtk_widget_set_vexpand(box, 1);
c.gtk_widget_set_hexpand(box, 1);
// Setup our constraint layout and attach it to the box
const layout = c.gtk_constraint_layout_new();
errdefer c.g_object_unref(layout);
c.gtk_widget_set_layout_manager(@ptrCast(box), layout);
// Setup our views table
const views = c.g_hash_table_new(c.g_str_hash, c.g_str_equal);
defer c.g_hash_table_unref(views);
// Add our widgets
for (widgets) |widget| {
c.gtk_widget_set_parent(widget.widget, box);
c.gtk_widget_set_name(widget.widget, widget.name);
_ = c.g_hash_table_insert(
views,
@constCast(@ptrCast(widget.name.ptr)),
widget.widget,
);
}
// Add all of our constraints for layout
var err_: ?*c.GError = null;
const list = c.gtk_constraint_layout_add_constraints_from_descriptionv(
@ptrCast(layout),
vfl.ptr,
vfl.len,
8,
8,
views,
&err_,
);
if (err_) |err| {
defer c.g_error_free(err);
log.warn("error building view message={s}", .{err.message});
return error.OperationFailed;
}
c.g_list_free(list);
return .{ .root = box };
}

View File

@ -1,8 +0,0 @@
/// Imported C API directly from header files
pub const c = @cImport({
@cInclude("gtk/gtk.h");
@cInclude("adwaita.h");
// compatibility
@cInclude("ghostty_gtk_compat.h");
});

View File

@ -60,6 +60,7 @@ pub const VersionedBuilderXML = struct {
}; };
pub const ui_files = [_]VersionedBuilderXML{ pub const ui_files = [_]VersionedBuilderXML{
.{ .major = 1, .minor = 2, .name = "config-errors-dialog" },
.{ .major = 1, .minor = 2, .name = "ccw-osc-52-read" }, .{ .major = 1, .minor = 2, .name = "ccw-osc-52-read" },
.{ .major = 1, .minor = 2, .name = "ccw-osc-52-write" }, .{ .major = 1, .minor = 2, .name = "ccw-osc-52-write" },
.{ .major = 1, .minor = 2, .name = "ccw-paste" }, .{ .major = 1, .minor = 2, .name = "ccw-paste" },
@ -73,6 +74,7 @@ pub const VersionedBlueprint = struct {
pub const blueprint_files = [_]VersionedBlueprint{ pub const blueprint_files = [_]VersionedBlueprint{
.{ .major = 1, .minor = 5, .name = "prompt-title-dialog" }, .{ .major = 1, .minor = 5, .name = "prompt-title-dialog" },
.{ .major = 1, .minor = 5, .name = "config-errors-dialog" },
.{ .major = 1, .minor = 0, .name = "menu-surface-context_menu" }, .{ .major = 1, .minor = 0, .name = "menu-surface-context_menu" },
.{ .major = 1, .minor = 0, .name = "menu-window-titlebar_menu" }, .{ .major = 1, .minor = 0, .name = "menu-window-titlebar_menu" },
.{ .major = 1, .minor = 5, .name = "ccw-osc-52-read" }, .{ .major = 1, .minor = 5, .name = "ccw-osc-52-read" },

View File

@ -2,12 +2,14 @@ const std = @import("std");
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const assert = std.debug.assert; const assert = std.debug.assert;
const gtk = @import("gtk");
const build_config = @import("../../build_config.zig"); const build_config = @import("../../build_config.zig");
const i18n = @import("../../os/main.zig").i18n;
const App = @import("App.zig"); const App = @import("App.zig");
const Surface = @import("Surface.zig"); const Surface = @import("Surface.zig");
const TerminalWindow = @import("Window.zig"); const TerminalWindow = @import("Window.zig");
const ImguiWidget = @import("ImguiWidget.zig"); const ImguiWidget = @import("ImguiWidget.zig");
const c = @import("c.zig").c;
const CoreInspector = @import("../../inspector/main.zig").Inspector; const CoreInspector = @import("../../inspector/main.zig").Inspector;
const log = std.log.scoped(.inspector); const log = std.log.scoped(.inspector);
@ -124,7 +126,7 @@ pub const Inspector = struct {
/// A dedicated window to hold an inspector instance. /// A dedicated window to hold an inspector instance.
const Window = struct { const Window = struct {
inspector: *Inspector, inspector: *Inspector,
window: *c.GtkWindow, window: *gtk.ApplicationWindow,
imgui_widget: ImguiWidget, imgui_widget: ImguiWidget,
pub fn init(self: *Window, inspector: *Inspector) !void { pub fn init(self: *Window, inspector: *Inspector) !void {
@ -136,15 +138,14 @@ const Window = struct {
}; };
// Create the window // Create the window
const window = c.gtk_application_window_new(@ptrCast(@alignCast(inspector.surface.app.app))); self.window = gtk.ApplicationWindow.new(inspector.surface.app.app.as(gtk.Application));
const gtk_window: *c.GtkWindow = @ptrCast(window); errdefer self.window.as(gtk.Window).destroy();
errdefer c.gtk_window_destroy(gtk_window);
self.window = gtk_window; self.window.as(gtk.Window).setTitle(i18n._("Ghostty: Terminal Inspector"));
c.gtk_window_set_title(gtk_window, "Ghostty: Terminal Inspector"); self.window.as(gtk.Window).setDefaultSize(1000, 600);
c.gtk_window_set_default_size(gtk_window, 1000, 600); self.window.as(gtk.Window).setIconName(build_config.bundle_id);
c.gtk_window_set_icon_name(gtk_window, build_config.bundle_id); self.window.as(gtk.Widget).addCssClass("window");
c.gtk_widget_add_css_class(@ptrCast(@alignCast(gtk_window)), "window"); self.window.as(gtk.Widget).addCssClass("inspector-window");
c.gtk_widget_add_css_class(@ptrCast(@alignCast(gtk_window)), "inspector-window");
// Initialize our imgui widget // Initialize our imgui widget
try self.imgui_widget.init(); try self.imgui_widget.init();
@ -154,11 +155,10 @@ const Window = struct {
CoreInspector.setup(); CoreInspector.setup();
// Signals // Signals
_ = c.g_signal_connect_data(window, "destroy", c.G_CALLBACK(&gtkDestroy), self, null, c.G_CONNECT_DEFAULT); _ = gtk.Widget.signals.destroy.connect(self.window, *Window, gtkDestroy, self, .{});
// Show the window // Show the window
c.gtk_window_set_child(gtk_window, @ptrCast(self.imgui_widget.gl_area)); self.window.as(gtk.Window).setChild(self.imgui_widget.gl_area.as(gtk.Widget));
c.gtk_widget_show(window); self.window.as(gtk.Window).present();
} }
pub fn deinit(self: *Window) void { pub fn deinit(self: *Window) void {
@ -166,7 +166,7 @@ const Window = struct {
} }
pub fn close(self: *const Window) void { pub fn close(self: *const Window) void {
c.gtk_window_destroy(self.window); self.window.as(gtk.Window).destroy();
} }
fn imguiRender(ud: ?*anyopaque) void { fn imguiRender(ud: ?*anyopaque) void {
@ -177,11 +177,8 @@ const Window = struct {
} }
/// "destroy" signal for the window /// "destroy" signal for the window
fn gtkDestroy(v: *c.GtkWidget, ud: ?*anyopaque) callconv(.C) void { fn gtkDestroy(_: *gtk.ApplicationWindow, self: *Window) callconv(.C) void {
_ = v;
log.debug("window destroy", .{}); log.debug("window destroy", .{});
const self: *Window = @ptrCast(@alignCast(ud.?));
self.deinit(); self.deinit();
} }
}; };

View File

@ -6,7 +6,6 @@ const glib = @import("glib");
const gtk = @import("gtk"); const gtk = @import("gtk");
const input = @import("../../input.zig"); const input = @import("../../input.zig");
const c = @import("c.zig").c;
const winproto = @import("winproto.zig"); const winproto = @import("winproto.zig");
/// Returns a GTK accelerator string from a trigger. /// Returns a GTK accelerator string from a trigger.
@ -24,12 +23,12 @@ pub fn accelFromTrigger(buf: []u8, trigger: input.Binding.Trigger) !?[:0]const u
switch (trigger.key) { switch (trigger.key) {
.physical, .translated => |k| { .physical, .translated => |k| {
const keyval = keyvalFromKey(k) orelse return null; const keyval = keyvalFromKey(k) orelse return null;
try writer.writeAll(std.mem.sliceTo(c.gdk_keyval_name(keyval), 0)); try writer.writeAll(std.mem.span(gdk.keyvalName(keyval) orelse return null));
}, },
.unicode => |cp| { .unicode => |cp| {
if (c.gdk_keyval_name(cp)) |name| { if (gdk.keyvalName(cp)) |name| {
try writer.writeAll(std.mem.sliceTo(name, 0)); try writer.writeAll(std.mem.span(name));
} else { } else {
try writer.print("{u}", .{cp}); try writer.print("{u}", .{cp});
} }
@ -170,7 +169,7 @@ pub fn eventMods(
} }
/// Returns an input key from a keyval or null if we don't have a mapping. /// Returns an input key from a keyval or null if we don't have a mapping.
pub fn keyFromKeyval(keyval: c.guint) ?input.Key { pub fn keyFromKeyval(keyval: c_uint) ?input.Key {
for (keymap) |entry| { for (keymap) |entry| {
if (entry[0] == keyval) return entry[1]; if (entry[0] == keyval) return entry[1];
} }
@ -179,7 +178,7 @@ pub fn keyFromKeyval(keyval: c.guint) ?input.Key {
} }
/// Returns a keyval from an input key or null if we don't have a mapping. /// Returns a keyval from an input key or null if we don't have a mapping.
pub fn keyvalFromKey(key: input.Key) ?c.guint { pub fn keyvalFromKey(key: input.Key) ?c_uint {
switch (key) { switch (key) {
inline else => |key_comptime| { inline else => |key_comptime| {
return comptime value: { return comptime value: {
@ -211,145 +210,145 @@ test "accelFromTrigger" {
/// A raw entry in the keymap. Our keymap contains mappings between /// A raw entry in the keymap. Our keymap contains mappings between
/// GDK keys and our own key enum. /// GDK keys and our own key enum.
const RawEntry = struct { c.guint, input.Key }; const RawEntry = struct { c_uint, input.Key };
const keymap: []const RawEntry = &.{ const keymap: []const RawEntry = &.{
.{ c.GDK_KEY_a, .a }, .{ gdk.KEY_a, .a },
.{ c.GDK_KEY_b, .b }, .{ gdk.KEY_b, .b },
.{ c.GDK_KEY_c, .c }, .{ gdk.KEY_c, .c },
.{ c.GDK_KEY_d, .d }, .{ gdk.KEY_d, .d },
.{ c.GDK_KEY_e, .e }, .{ gdk.KEY_e, .e },
.{ c.GDK_KEY_f, .f }, .{ gdk.KEY_f, .f },
.{ c.GDK_KEY_g, .g }, .{ gdk.KEY_g, .g },
.{ c.GDK_KEY_h, .h }, .{ gdk.KEY_h, .h },
.{ c.GDK_KEY_i, .i }, .{ gdk.KEY_i, .i },
.{ c.GDK_KEY_j, .j }, .{ gdk.KEY_j, .j },
.{ c.GDK_KEY_k, .k }, .{ gdk.KEY_k, .k },
.{ c.GDK_KEY_l, .l }, .{ gdk.KEY_l, .l },
.{ c.GDK_KEY_m, .m }, .{ gdk.KEY_m, .m },
.{ c.GDK_KEY_n, .n }, .{ gdk.KEY_n, .n },
.{ c.GDK_KEY_o, .o }, .{ gdk.KEY_o, .o },
.{ c.GDK_KEY_p, .p }, .{ gdk.KEY_p, .p },
.{ c.GDK_KEY_q, .q }, .{ gdk.KEY_q, .q },
.{ c.GDK_KEY_r, .r }, .{ gdk.KEY_r, .r },
.{ c.GDK_KEY_s, .s }, .{ gdk.KEY_s, .s },
.{ c.GDK_KEY_t, .t }, .{ gdk.KEY_t, .t },
.{ c.GDK_KEY_u, .u }, .{ gdk.KEY_u, .u },
.{ c.GDK_KEY_v, .v }, .{ gdk.KEY_v, .v },
.{ c.GDK_KEY_w, .w }, .{ gdk.KEY_w, .w },
.{ c.GDK_KEY_x, .x }, .{ gdk.KEY_x, .x },
.{ c.GDK_KEY_y, .y }, .{ gdk.KEY_y, .y },
.{ c.GDK_KEY_z, .z }, .{ gdk.KEY_z, .z },
.{ c.GDK_KEY_0, .zero }, .{ gdk.KEY_0, .zero },
.{ c.GDK_KEY_1, .one }, .{ gdk.KEY_1, .one },
.{ c.GDK_KEY_2, .two }, .{ gdk.KEY_2, .two },
.{ c.GDK_KEY_3, .three }, .{ gdk.KEY_3, .three },
.{ c.GDK_KEY_4, .four }, .{ gdk.KEY_4, .four },
.{ c.GDK_KEY_5, .five }, .{ gdk.KEY_5, .five },
.{ c.GDK_KEY_6, .six }, .{ gdk.KEY_6, .six },
.{ c.GDK_KEY_7, .seven }, .{ gdk.KEY_7, .seven },
.{ c.GDK_KEY_8, .eight }, .{ gdk.KEY_8, .eight },
.{ c.GDK_KEY_9, .nine }, .{ gdk.KEY_9, .nine },
.{ c.GDK_KEY_semicolon, .semicolon }, .{ gdk.KEY_semicolon, .semicolon },
.{ c.GDK_KEY_space, .space }, .{ gdk.KEY_space, .space },
.{ c.GDK_KEY_apostrophe, .apostrophe }, .{ gdk.KEY_apostrophe, .apostrophe },
.{ c.GDK_KEY_comma, .comma }, .{ gdk.KEY_comma, .comma },
.{ c.GDK_KEY_grave, .grave_accent }, .{ gdk.KEY_grave, .grave_accent },
.{ c.GDK_KEY_period, .period }, .{ gdk.KEY_period, .period },
.{ c.GDK_KEY_slash, .slash }, .{ gdk.KEY_slash, .slash },
.{ c.GDK_KEY_minus, .minus }, .{ gdk.KEY_minus, .minus },
.{ c.GDK_KEY_equal, .equal }, .{ gdk.KEY_equal, .equal },
.{ c.GDK_KEY_bracketleft, .left_bracket }, .{ gdk.KEY_bracketleft, .left_bracket },
.{ c.GDK_KEY_bracketright, .right_bracket }, .{ gdk.KEY_bracketright, .right_bracket },
.{ c.GDK_KEY_backslash, .backslash }, .{ gdk.KEY_backslash, .backslash },
.{ c.GDK_KEY_Up, .up }, .{ gdk.KEY_Up, .up },
.{ c.GDK_KEY_Down, .down }, .{ gdk.KEY_Down, .down },
.{ c.GDK_KEY_Right, .right }, .{ gdk.KEY_Right, .right },
.{ c.GDK_KEY_Left, .left }, .{ gdk.KEY_Left, .left },
.{ c.GDK_KEY_Home, .home }, .{ gdk.KEY_Home, .home },
.{ c.GDK_KEY_End, .end }, .{ gdk.KEY_End, .end },
.{ c.GDK_KEY_Insert, .insert }, .{ gdk.KEY_Insert, .insert },
.{ c.GDK_KEY_Delete, .delete }, .{ gdk.KEY_Delete, .delete },
.{ c.GDK_KEY_Caps_Lock, .caps_lock }, .{ gdk.KEY_Caps_Lock, .caps_lock },
.{ c.GDK_KEY_Scroll_Lock, .scroll_lock }, .{ gdk.KEY_Scroll_Lock, .scroll_lock },
.{ c.GDK_KEY_Num_Lock, .num_lock }, .{ gdk.KEY_Num_Lock, .num_lock },
.{ c.GDK_KEY_Page_Up, .page_up }, .{ gdk.KEY_Page_Up, .page_up },
.{ c.GDK_KEY_Page_Down, .page_down }, .{ gdk.KEY_Page_Down, .page_down },
.{ c.GDK_KEY_Escape, .escape }, .{ gdk.KEY_Escape, .escape },
.{ c.GDK_KEY_Return, .enter }, .{ gdk.KEY_Return, .enter },
.{ c.GDK_KEY_Tab, .tab }, .{ gdk.KEY_Tab, .tab },
.{ c.GDK_KEY_BackSpace, .backspace }, .{ gdk.KEY_BackSpace, .backspace },
.{ c.GDK_KEY_Print, .print_screen }, .{ gdk.KEY_Print, .print_screen },
.{ c.GDK_KEY_Pause, .pause }, .{ gdk.KEY_Pause, .pause },
.{ c.GDK_KEY_F1, .f1 }, .{ gdk.KEY_F1, .f1 },
.{ c.GDK_KEY_F2, .f2 }, .{ gdk.KEY_F2, .f2 },
.{ c.GDK_KEY_F3, .f3 }, .{ gdk.KEY_F3, .f3 },
.{ c.GDK_KEY_F4, .f4 }, .{ gdk.KEY_F4, .f4 },
.{ c.GDK_KEY_F5, .f5 }, .{ gdk.KEY_F5, .f5 },
.{ c.GDK_KEY_F6, .f6 }, .{ gdk.KEY_F6, .f6 },
.{ c.GDK_KEY_F7, .f7 }, .{ gdk.KEY_F7, .f7 },
.{ c.GDK_KEY_F8, .f8 }, .{ gdk.KEY_F8, .f8 },
.{ c.GDK_KEY_F9, .f9 }, .{ gdk.KEY_F9, .f9 },
.{ c.GDK_KEY_F10, .f10 }, .{ gdk.KEY_F10, .f10 },
.{ c.GDK_KEY_F11, .f11 }, .{ gdk.KEY_F11, .f11 },
.{ c.GDK_KEY_F12, .f12 }, .{ gdk.KEY_F12, .f12 },
.{ c.GDK_KEY_F13, .f13 }, .{ gdk.KEY_F13, .f13 },
.{ c.GDK_KEY_F14, .f14 }, .{ gdk.KEY_F14, .f14 },
.{ c.GDK_KEY_F15, .f15 }, .{ gdk.KEY_F15, .f15 },
.{ c.GDK_KEY_F16, .f16 }, .{ gdk.KEY_F16, .f16 },
.{ c.GDK_KEY_F17, .f17 }, .{ gdk.KEY_F17, .f17 },
.{ c.GDK_KEY_F18, .f18 }, .{ gdk.KEY_F18, .f18 },
.{ c.GDK_KEY_F19, .f19 }, .{ gdk.KEY_F19, .f19 },
.{ c.GDK_KEY_F20, .f20 }, .{ gdk.KEY_F20, .f20 },
.{ c.GDK_KEY_F21, .f21 }, .{ gdk.KEY_F21, .f21 },
.{ c.GDK_KEY_F22, .f22 }, .{ gdk.KEY_F22, .f22 },
.{ c.GDK_KEY_F23, .f23 }, .{ gdk.KEY_F23, .f23 },
.{ c.GDK_KEY_F24, .f24 }, .{ gdk.KEY_F24, .f24 },
.{ c.GDK_KEY_F25, .f25 }, .{ gdk.KEY_F25, .f25 },
.{ c.GDK_KEY_KP_0, .kp_0 }, .{ gdk.KEY_KP_0, .kp_0 },
.{ c.GDK_KEY_KP_1, .kp_1 }, .{ gdk.KEY_KP_1, .kp_1 },
.{ c.GDK_KEY_KP_2, .kp_2 }, .{ gdk.KEY_KP_2, .kp_2 },
.{ c.GDK_KEY_KP_3, .kp_3 }, .{ gdk.KEY_KP_3, .kp_3 },
.{ c.GDK_KEY_KP_4, .kp_4 }, .{ gdk.KEY_KP_4, .kp_4 },
.{ c.GDK_KEY_KP_5, .kp_5 }, .{ gdk.KEY_KP_5, .kp_5 },
.{ c.GDK_KEY_KP_6, .kp_6 }, .{ gdk.KEY_KP_6, .kp_6 },
.{ c.GDK_KEY_KP_7, .kp_7 }, .{ gdk.KEY_KP_7, .kp_7 },
.{ c.GDK_KEY_KP_8, .kp_8 }, .{ gdk.KEY_KP_8, .kp_8 },
.{ c.GDK_KEY_KP_9, .kp_9 }, .{ gdk.KEY_KP_9, .kp_9 },
.{ c.GDK_KEY_KP_Decimal, .kp_decimal }, .{ gdk.KEY_KP_Decimal, .kp_decimal },
.{ c.GDK_KEY_KP_Divide, .kp_divide }, .{ gdk.KEY_KP_Divide, .kp_divide },
.{ c.GDK_KEY_KP_Multiply, .kp_multiply }, .{ gdk.KEY_KP_Multiply, .kp_multiply },
.{ c.GDK_KEY_KP_Subtract, .kp_subtract }, .{ gdk.KEY_KP_Subtract, .kp_subtract },
.{ c.GDK_KEY_KP_Add, .kp_add }, .{ gdk.KEY_KP_Add, .kp_add },
.{ c.GDK_KEY_KP_Enter, .kp_enter }, .{ gdk.KEY_KP_Enter, .kp_enter },
.{ c.GDK_KEY_KP_Equal, .kp_equal }, .{ gdk.KEY_KP_Equal, .kp_equal },
.{ c.GDK_KEY_KP_Separator, .kp_separator }, .{ gdk.KEY_KP_Separator, .kp_separator },
.{ c.GDK_KEY_KP_Left, .kp_left }, .{ gdk.KEY_KP_Left, .kp_left },
.{ c.GDK_KEY_KP_Right, .kp_right }, .{ gdk.KEY_KP_Right, .kp_right },
.{ c.GDK_KEY_KP_Up, .kp_up }, .{ gdk.KEY_KP_Up, .kp_up },
.{ c.GDK_KEY_KP_Down, .kp_down }, .{ gdk.KEY_KP_Down, .kp_down },
.{ c.GDK_KEY_KP_Page_Up, .kp_page_up }, .{ gdk.KEY_KP_Page_Up, .kp_page_up },
.{ c.GDK_KEY_KP_Page_Down, .kp_page_down }, .{ gdk.KEY_KP_Page_Down, .kp_page_down },
.{ c.GDK_KEY_KP_Home, .kp_home }, .{ gdk.KEY_KP_Home, .kp_home },
.{ c.GDK_KEY_KP_End, .kp_end }, .{ gdk.KEY_KP_End, .kp_end },
.{ c.GDK_KEY_KP_Insert, .kp_insert }, .{ gdk.KEY_KP_Insert, .kp_insert },
.{ c.GDK_KEY_KP_Delete, .kp_delete }, .{ gdk.KEY_KP_Delete, .kp_delete },
.{ c.GDK_KEY_KP_Begin, .kp_begin }, .{ gdk.KEY_KP_Begin, .kp_begin },
.{ c.GDK_KEY_Shift_L, .left_shift }, .{ gdk.KEY_Shift_L, .left_shift },
.{ c.GDK_KEY_Control_L, .left_control }, .{ gdk.KEY_Control_L, .left_control },
.{ c.GDK_KEY_Alt_L, .left_alt }, .{ gdk.KEY_Alt_L, .left_alt },
.{ c.GDK_KEY_Super_L, .left_super }, .{ gdk.KEY_Super_L, .left_super },
.{ c.GDK_KEY_Shift_R, .right_shift }, .{ gdk.KEY_Shift_R, .right_shift },
.{ c.GDK_KEY_Control_R, .right_control }, .{ gdk.KEY_Control_R, .right_control },
.{ c.GDK_KEY_Alt_R, .right_alt }, .{ gdk.KEY_Alt_R, .right_alt },
.{ c.GDK_KEY_Super_R, .right_super }, .{ gdk.KEY_Super_R, .right_super },
// TODO: media keys // TODO: media keys
}; };

View File

@ -0,0 +1,28 @@
using Gtk 4.0;
using Adw 1;
Adw.MessageDialog config_errors_dialog {
heading: _("Configuration Errors");
body: _("One or more configuration errors were found. Please review the errors below, and either reload your configuration or ignore these errors.");
responses [
ignore: _("Ignore"),
reload: _("Reload Configuration") suggested,
]
extra-child: ScrolledWindow {
min-content-width: 500;
min-content-height: 100;
TextView {
editable: false;
cursor-visible: false;
top-margin: 8;
bottom-margin: 8;
left-margin: 8;
right-margin: 8;
buffer: TextBuffer error_message { };
}
};
}

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
DO NOT EDIT!
This file was @generated by blueprint-compiler. Instead, edit the
corresponding .blp file and regenerate this file with blueprint-compiler.
-->
<interface>
<requires lib="gtk" version="4.0"/>
<object class="AdwMessageDialog" id="config_errors_dialog">
<property name="heading" translatable="yes">Configuration Errors</property>
<property name="body" translatable="yes">One or more configuration errors were found. Please review the errors below, and either reload your configuration or ignore these errors.</property>
<responses>
<response id="ignore" translatable="yes">Ignore</response>
<response id="reload" translatable="yes" appearance="suggested">Reload Configuration</response>
</responses>
<property name="extra-child">
<object class="GtkScrolledWindow">
<property name="min-content-width">500</property>
<property name="min-content-height">100</property>
<child>
<object class="GtkTextView">
<property name="editable">false</property>
<property name="cursor-visible">false</property>
<property name="top-margin">8</property>
<property name="bottom-margin">8</property>
<property name="left-margin">8</property>
<property name="right-margin">8</property>
<property name="buffer">
<object class="GtkTextBuffer" id="error_message"></object>
</property>
</object>
</child>
</object>
</property>
</object>
</interface>

View File

@ -0,0 +1,28 @@
using Gtk 4.0;
using Adw 1;
Adw.AlertDialog config_errors_dialog {
heading: _("Configuration Errors");
body: _("One or more configuration errors were found. Please review the errors below, and either reload your configuration or ignore these errors.");
responses [
ignore: _("Ignore"),
reload: _("Reload Configuration") suggested,
]
extra-child: ScrolledWindow {
min-content-width: 500;
min-content-height: 100;
TextView {
editable: false;
cursor-visible: false;
top-margin: 8;
bottom-margin: 8;
left-margin: 8;
right-margin: 8;
buffer: TextBuffer error_message { };
}
};
}

View File

@ -11,11 +11,17 @@ const gtk = @import("gtk");
const log = std.log.scoped(.gtk); const log = std.log.scoped(.gtk);
pub const version: std.SemanticVersion = .{
.major = c.GTK_MAJOR_VERSION,
.minor = c.GTK_MINOR_VERSION,
.patch = c.GTK_MICRO_VERSION,
};
pub fn logVersion() void { pub fn logVersion() void {
log.info("GTK version build={d}.{d}.{d} runtime={d}.{d}.{d}", .{ log.info("GTK version build={d}.{d}.{d} runtime={d}.{d}.{d}", .{
c.GTK_MAJOR_VERSION, version.major,
c.GTK_MINOR_VERSION, version.minor,
c.GTK_MICRO_VERSION, version.patch,
gtk.getMajorVersion(), gtk.getMajorVersion(),
gtk.getMinorVersion(), gtk.getMinorVersion(),
gtk.getMicroVersion(), gtk.getMicroVersion(),
@ -45,10 +51,11 @@ pub inline fn atLeast(
// we can return false immediately. This prevents us from // we can return false immediately. This prevents us from
// compiling against unknown symbols and makes runtime checks // compiling against unknown symbols and makes runtime checks
// very slightly faster. // very slightly faster.
if (comptime c.GTK_MAJOR_VERSION < major or if (comptime version.order(.{
(c.GTK_MAJOR_VERSION == major and c.GTK_MINOR_VERSION < minor) or .major = major,
(c.GTK_MAJOR_VERSION == major and c.GTK_MINOR_VERSION == minor and c.GTK_MICRO_VERSION < micro)) .minor = minor,
return false; .patch = micro,
}) == .lt) return false;
// If we're in comptime then we can't check the runtime version. // If we're in comptime then we can't check the runtime version.
if (@inComptime()) return true; if (@inComptime()) return true;

View File

@ -6,7 +6,10 @@ const build_config = @import("../build_config.zig");
const internal_os = @import("../os/main.zig"); const internal_os = @import("../os/main.zig");
const xev = @import("../global.zig").xev; const xev = @import("../global.zig").xev;
const renderer = @import("../renderer.zig"); const renderer = @import("../renderer.zig");
const gtk = if (build_config.app_runtime == .gtk) @import("../apprt/gtk/c.zig").c else void;
const gtk_version = @import("../apprt/gtk/version.zig").version;
const gtk = @import("gtk");
const adw = @import("adw");
pub const Options = struct {}; pub const Options = struct {};
@ -42,23 +45,23 @@ pub fn run(alloc: Allocator) !u8 {
try stdout.print(" - desktop env: {s}\n", .{@tagName(internal_os.desktopEnvironment())}); try stdout.print(" - desktop env: {s}\n", .{@tagName(internal_os.desktopEnvironment())});
try stdout.print(" - GTK version:\n", .{}); try stdout.print(" - GTK version:\n", .{});
try stdout.print(" build : {d}.{d}.{d}\n", .{ try stdout.print(" build : {d}.{d}.{d}\n", .{
gtk.GTK_MAJOR_VERSION, gtk_version.major,
gtk.GTK_MINOR_VERSION, gtk_version.minor,
gtk.GTK_MICRO_VERSION, gtk_version.patch,
}); });
try stdout.print(" runtime : {d}.{d}.{d}\n", .{ try stdout.print(" runtime : {d}.{d}.{d}\n", .{
gtk.gtk_get_major_version(), gtk.getMajorVersion(),
gtk.gtk_get_minor_version(), gtk.getMinorVersion(),
gtk.gtk_get_micro_version(), gtk.getMicroVersion(),
}); });
try stdout.print(" - libadwaita : enabled\n", .{}); try stdout.print(" - libadwaita : enabled\n", .{});
try stdout.print(" build : {s}\n", .{ try stdout.print(" build : {s}\n", .{
gtk.ADW_VERSION_S, adw.VERSION_S,
}); });
try stdout.print(" runtime : {}.{}.{}\n", .{ try stdout.print(" runtime : {}.{}.{}\n", .{
gtk.adw_get_major_version(), adw.getMajorVersion(),
gtk.adw_get_minor_version(), adw.getMinorVersion(),
gtk.adw_get_micro_version(), adw.getMicroVersion(),
}); });
if (comptime build_options.x11) { if (comptime build_options.x11) {
try stdout.print(" - libX11 : enabled\n", .{}); try stdout.print(" - libX11 : enabled\n", .{});