mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-09-05 19:08:17 +00:00
gtk: convert Surface to zig-gobject (#6634)
Marking this is as draft because I want to test this further before saying that it's ready, but putting it out there for feedback.
This commit is contained in:
@@ -10,11 +10,12 @@
|
||||
/// (event loop) along with any global app state.
|
||||
const App = @This();
|
||||
|
||||
const gtk = @import("gtk");
|
||||
const adw = @import("adw");
|
||||
const gdk = @import("gdk");
|
||||
const gio = @import("gio");
|
||||
const glib = @import("glib");
|
||||
const gobject = @import("gobject");
|
||||
const adw = @import("adw");
|
||||
const gtk = @import("gtk");
|
||||
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
@@ -64,7 +65,7 @@ winproto: winprotopkg.App,
|
||||
single_instance: bool,
|
||||
|
||||
/// The "none" cursor. We use one that is shared across the entire app.
|
||||
cursor_none: ?*c.GdkCursor,
|
||||
cursor_none: ?*gdk.Cursor,
|
||||
|
||||
/// The configuration errors window, if it is currently open.
|
||||
config_errors_window: ?*ConfigErrorsWindow = null,
|
||||
@@ -280,8 +281,8 @@ pub fn init(core_app: *CoreApp, opts: Options) !App {
|
||||
};
|
||||
|
||||
// The "none" cursor is used for hiding the cursor
|
||||
const cursor_none = c.gdk_cursor_new_from_name("none", null);
|
||||
errdefer if (cursor_none) |cursor| c.g_object_unref(cursor);
|
||||
const cursor_none = gdk.Cursor.newFromName("none", null);
|
||||
errdefer if (cursor_none) |cursor| cursor.unref();
|
||||
|
||||
const single_instance = switch (config.@"gtk-single-instance") {
|
||||
.true => true,
|
||||
|
@@ -370,8 +370,9 @@ fn keyEvent(
|
||||
cimgui.c.igSetCurrentContext(self.ig_ctx);
|
||||
const io: *cimgui.c.ImGuiIO = cimgui.c.igGetIO();
|
||||
|
||||
// FIXME: when this file get converted to zig-gobject
|
||||
// Translate the GTK mods and update the modifiers on every keypress
|
||||
const mods = key.translateMods(gtk_mods);
|
||||
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_LeftCtrl, mods.ctrl);
|
||||
cimgui.c.ImGuiIO_AddKeyEvent(io, cimgui.c.ImGuiKey_LeftAlt, mods.alt);
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -73,8 +73,9 @@ pub fn init(self: *Tab, window: *Window, parent_: ?*CoreSurface) !void {
|
||||
surface.container = .{ .tab_ = self };
|
||||
self.elem = .{ .surface = surface };
|
||||
|
||||
// FIXME: when Tab.zig is converted to zig-gobject
|
||||
// Add Surface to the Tab
|
||||
c.gtk_box_append(self.box, surface.primaryWidget());
|
||||
c.gtk_box_append(self.box, @ptrCast(@alignCast(surface.primaryWidget())));
|
||||
|
||||
// Set the userdata of the box to point to this tab.
|
||||
c.g_object_set_data(@ptrCast(box_widget), GHOSTTY_TAB, self);
|
||||
@@ -103,10 +104,11 @@ pub fn destroy(self: *Tab, alloc: Allocator) void {
|
||||
/// Replace the surface element that this tab is showing.
|
||||
pub fn replaceElem(self: *Tab, elem: Surface.Container.Elem) void {
|
||||
// Remove our previous widget
|
||||
c.gtk_box_remove(self.box, self.elem.widget());
|
||||
// FIXME: when Tab.zig is converted to zig-gobject
|
||||
c.gtk_box_remove(self.box, @ptrCast(@alignCast(self.elem.widget())));
|
||||
|
||||
// Add our new one
|
||||
c.gtk_box_append(self.box, elem.widget());
|
||||
c.gtk_box_append(self.box, @ptrCast(@alignCast(elem.widget())));
|
||||
self.elem = elem;
|
||||
}
|
||||
|
||||
|
@@ -10,6 +10,7 @@ const builtin = @import("builtin");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const assert = std.debug.assert;
|
||||
|
||||
const gdk = @import("gdk");
|
||||
const gio = @import("gio");
|
||||
const glib = @import("glib");
|
||||
const gobject = @import("gobject");
|
||||
@@ -297,15 +298,21 @@ pub fn init(self: *Window, app: *App) !void {
|
||||
// We register a key event controller with the window so
|
||||
// we can catch key events when our surface may not be
|
||||
// focused (i.e. when the libadw tab overview is shown).
|
||||
const ec_key_press = c.gtk_event_controller_key_new();
|
||||
errdefer c.g_object_unref(ec_key_press);
|
||||
c.gtk_widget_add_controller(gtk_widget, ec_key_press);
|
||||
const ec_key_press = gtk.EventControllerKey.new();
|
||||
errdefer ec_key_press.unref();
|
||||
c.gtk_widget_add_controller(gtk_widget, @ptrCast(@alignCast(ec_key_press)));
|
||||
|
||||
// All of our events
|
||||
_ = c.g_signal_connect_data(self.window, "realize", c.G_CALLBACK(>kRealize), self, null, c.G_CONNECT_DEFAULT);
|
||||
_ = c.g_signal_connect_data(self.window, "close-request", c.G_CALLBACK(>kCloseRequest), self, null, c.G_CONNECT_DEFAULT);
|
||||
_ = c.g_signal_connect_data(self.window, "destroy", c.G_CALLBACK(>kDestroy), self, null, c.G_CONNECT_DEFAULT);
|
||||
_ = c.g_signal_connect_data(ec_key_press, "key-pressed", c.G_CALLBACK(>kKeyPressed), self, null, c.G_CONNECT_DEFAULT);
|
||||
_ = gtk.EventControllerKey.signals.key_pressed.connect(
|
||||
ec_key_press,
|
||||
*Window,
|
||||
gtkKeyPressed,
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
|
||||
// Our actions for the menu
|
||||
initActions(self);
|
||||
@@ -865,14 +872,12 @@ fn gtkDestroy(v: *c.GtkWidget, ud: ?*anyopaque) callconv(.C) void {
|
||||
}
|
||||
|
||||
fn gtkKeyPressed(
|
||||
ec_key: *c.GtkEventControllerKey,
|
||||
keyval: c.guint,
|
||||
keycode: c.guint,
|
||||
gtk_mods: c.GdkModifierType,
|
||||
ud: ?*anyopaque,
|
||||
ec_key: *gtk.EventControllerKey,
|
||||
keyval: c_uint,
|
||||
keycode: c_uint,
|
||||
gtk_mods: gdk.ModifierType,
|
||||
self: *Window,
|
||||
) callconv(.C) c.gboolean {
|
||||
const self = userdataSelf(ud.?);
|
||||
|
||||
// We only process window-level events currently for the tab
|
||||
// overview. This is primarily defensive programming because
|
||||
// I'm not 100% certain how our logic below will interact with
|
||||
|
@@ -1,5 +1,10 @@
|
||||
const std = @import("std");
|
||||
const build_options = @import("build_options");
|
||||
|
||||
const gdk = @import("gdk");
|
||||
const glib = @import("glib");
|
||||
const gtk = @import("gtk");
|
||||
|
||||
const input = @import("../../input.zig");
|
||||
const c = @import("c.zig").c;
|
||||
const winproto = @import("winproto.zig");
|
||||
@@ -37,63 +42,56 @@ pub fn accelFromTrigger(buf: []u8, trigger: input.Binding.Trigger) !?[:0]const u
|
||||
return slice[0 .. slice.len - 1 :0];
|
||||
}
|
||||
|
||||
pub fn translateMods(state: c.GdkModifierType) input.Mods {
|
||||
var mods: input.Mods = .{};
|
||||
if (state & c.GDK_SHIFT_MASK != 0) mods.shift = true;
|
||||
if (state & c.GDK_CONTROL_MASK != 0) mods.ctrl = true;
|
||||
if (state & c.GDK_ALT_MASK != 0) mods.alt = true;
|
||||
if (state & c.GDK_SUPER_MASK != 0) mods.super = true;
|
||||
|
||||
// Lock is dependent on the X settings but we just assume caps lock.
|
||||
if (state & c.GDK_LOCK_MASK != 0) mods.caps_lock = true;
|
||||
return mods;
|
||||
pub fn translateMods(state: gdk.ModifierType) input.Mods {
|
||||
return .{
|
||||
.shift = state.shift_mask,
|
||||
.ctrl = state.control_mask,
|
||||
.alt = state.alt_mask,
|
||||
.super = state.super_mask,
|
||||
// Lock is dependent on the X settings but we just assume caps lock.
|
||||
.caps_lock = state.lock_mask,
|
||||
};
|
||||
}
|
||||
|
||||
// Get the unshifted unicode value of the keyval. This is used
|
||||
// by the Kitty keyboard protocol.
|
||||
pub fn keyvalUnicodeUnshifted(
|
||||
widget: *c.GtkWidget,
|
||||
event: *c.GdkEvent,
|
||||
keycode: c.guint,
|
||||
widget: *gtk.Widget,
|
||||
event: *gdk.KeyEvent,
|
||||
keycode: u32,
|
||||
) u21 {
|
||||
const display = c.gtk_widget_get_display(widget);
|
||||
const display = widget.getDisplay();
|
||||
|
||||
// We need to get the currently active keyboard layout so we know
|
||||
// what group to look at.
|
||||
const layout = c.gdk_key_event_get_layout(@ptrCast(event));
|
||||
const layout = event.getLayout();
|
||||
|
||||
// Get all the possible keyboard mappings for this keycode. A keycode
|
||||
// is the physical key pressed.
|
||||
var keys: [*]c.GdkKeymapKey = undefined;
|
||||
var keyvals: [*]c.guint = undefined;
|
||||
var n_keys: c_int = 0;
|
||||
if (c.gdk_display_map_keycode(
|
||||
display,
|
||||
keycode,
|
||||
@ptrCast(&keys),
|
||||
@ptrCast(&keyvals),
|
||||
&n_keys,
|
||||
) == 0) return 0;
|
||||
// Get all the possible keyboard mappings for this keycode. A keycode is the
|
||||
// physical key pressed.
|
||||
var keys: [*]gdk.KeymapKey = undefined;
|
||||
var keyvals: [*]c_uint = undefined;
|
||||
var n_entries: c_int = 0;
|
||||
if (display.mapKeycode(keycode, &keys, &keyvals, &n_entries) == 0) return 0;
|
||||
|
||||
defer c.g_free(keys);
|
||||
defer c.g_free(keyvals);
|
||||
defer glib.free(keys);
|
||||
defer glib.free(keyvals);
|
||||
|
||||
// debugging:
|
||||
// log.debug("layout={}", .{layout});
|
||||
// for (0..@intCast(n_keys)) |i| {
|
||||
// log.debug("keymap key={} codepoint={x}", .{
|
||||
// std.log.debug("layout={}", .{layout});
|
||||
// for (0..@intCast(n_entries)) |i| {
|
||||
// std.log.debug("keymap key={} codepoint={x}", .{
|
||||
// keys[i],
|
||||
// c.gdk_keyval_to_unicode(keyvals[i]),
|
||||
// gdk.keyvalToUnicode(keyvals[i]),
|
||||
// });
|
||||
// }
|
||||
|
||||
for (0..@intCast(n_keys)) |i| {
|
||||
if (keys[i].group == layout and
|
||||
keys[i].level == 0)
|
||||
for (0..@intCast(n_entries)) |i| {
|
||||
if (keys[i].f_group == layout and
|
||||
keys[i].f_level == 0)
|
||||
{
|
||||
return std.math.cast(
|
||||
u21,
|
||||
c.gdk_keyval_to_unicode(keyvals[i]),
|
||||
gdk.keyvalToUnicode(keyvals[i]),
|
||||
) orelse 0;
|
||||
}
|
||||
}
|
||||
@@ -105,16 +103,16 @@ pub fn keyvalUnicodeUnshifted(
|
||||
/// This requires a lot of context because the GdkEvent
|
||||
/// doesn't contain enough on its own.
|
||||
pub fn eventMods(
|
||||
event: *c.GdkEvent,
|
||||
event: *gdk.Event,
|
||||
physical_key: input.Key,
|
||||
gtk_mods: c.GdkModifierType,
|
||||
gtk_mods: gdk.ModifierType,
|
||||
action: input.Action,
|
||||
app_winproto: *winproto.App,
|
||||
) input.Mods {
|
||||
const device = c.gdk_event_get_device(event);
|
||||
const device = event.getDevice();
|
||||
|
||||
var mods = app_winproto.eventMods(device, gtk_mods);
|
||||
mods.num_lock = c.gdk_device_get_num_lock_state(device) == 1;
|
||||
mods.num_lock = if (device) |d| d.getNumLockState() != 0 else false;
|
||||
|
||||
// We use the physical key to determine sided modifiers. As
|
||||
// far as I can tell there's no other way to reliably determine
|
||||
|
@@ -1,6 +1,9 @@
|
||||
const std = @import("std");
|
||||
const build_options = @import("build_options");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const gdk = @import("gdk");
|
||||
|
||||
const c = @import("c.zig").c;
|
||||
const Config = @import("../../config.zig").Config;
|
||||
const input = @import("../../input.zig");
|
||||
@@ -52,8 +55,8 @@ pub const App = union(Protocol) {
|
||||
|
||||
pub fn eventMods(
|
||||
self: *App,
|
||||
device: ?*c.GdkDevice,
|
||||
gtk_mods: c.GdkModifierType,
|
||||
device: ?*gdk.Device,
|
||||
gtk_mods: gdk.ModifierType,
|
||||
) input.Mods {
|
||||
return switch (self.*) {
|
||||
inline else => |*v| v.eventMods(device, gtk_mods),
|
||||
|
@@ -1,5 +1,8 @@
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const gdk = @import("gdk");
|
||||
|
||||
const c = @import("../c.zig").c;
|
||||
const Config = @import("../../../config.zig").Config;
|
||||
const input = @import("../../../input.zig");
|
||||
@@ -24,8 +27,8 @@ pub const App = struct {
|
||||
|
||||
pub fn eventMods(
|
||||
_: *App,
|
||||
_: ?*c.GdkDevice,
|
||||
_: c.GdkModifierType,
|
||||
_: ?*gdk.Device,
|
||||
_: gdk.ModifierType,
|
||||
) ?input.Mods {
|
||||
return null;
|
||||
}
|
||||
|
@@ -1,8 +1,8 @@
|
||||
//! Wayland protocol implementation for the Ghostty GTK apprt.
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const build_options = @import("build_options");
|
||||
|
||||
const wayland = @import("wayland");
|
||||
const gtk = @import("gtk");
|
||||
const gtk4_layer_shell = @import("gtk4-layer-shell");
|
||||
@@ -86,8 +86,8 @@ pub const App = struct {
|
||||
|
||||
pub fn eventMods(
|
||||
_: *App,
|
||||
_: ?*c.GdkDevice,
|
||||
_: c.GdkModifierType,
|
||||
_: ?*gdk.Device,
|
||||
_: gdk.ModifierType,
|
||||
) ?input.Mods {
|
||||
return null;
|
||||
}
|
||||
|
@@ -3,6 +3,9 @@ const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const build_options = @import("build_options");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const gdk = @import("gdk");
|
||||
|
||||
const c = @import("../c.zig").c;
|
||||
const input = @import("../../../input.zig");
|
||||
const Config = @import("../../../config.zig").Config;
|
||||
@@ -117,8 +120,8 @@ pub const App = struct {
|
||||
/// event did not result in a modifier change).
|
||||
pub fn eventMods(
|
||||
self: App,
|
||||
device: ?*c.GdkDevice,
|
||||
gtk_mods: c.GdkModifierType,
|
||||
device: ?*gdk.Device,
|
||||
gtk_mods: gdk.ModifierType,
|
||||
) ?input.Mods {
|
||||
_ = device;
|
||||
_ = gtk_mods;
|
||||
|
@@ -455,12 +455,12 @@ pub fn add(
|
||||
.optimize = optimize,
|
||||
});
|
||||
const gobject_imports = .{
|
||||
.{ "gobject", "gobject2" },
|
||||
.{ "adw", "adw1" },
|
||||
.{ "gdk", "gdk4" },
|
||||
.{ "gio", "gio2" },
|
||||
.{ "glib", "glib2" },
|
||||
.{ "gobject", "gobject2" },
|
||||
.{ "gtk", "gtk4" },
|
||||
.{ "gdk", "gdk4" },
|
||||
.{ "adw", "adw1" },
|
||||
};
|
||||
inline for (gobject_imports) |import| {
|
||||
const name, const module = import;
|
||||
|
Reference in New Issue
Block a user