summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeffrey C. Ollie <jeff@ocjtech.us>2025-03-14 10:14:33 -0500
committerGitHub <noreply@github.com>2025-03-14 10:14:33 -0500
commitf37d1fd7eda5596226582e6e43c01c3251fd6eb9 (patch)
treed65575749dbaaafa8ec491d1c353739612ce7427
parentc2aac45848fbc98e40e0c9b5f171298f3fc44ca9 (diff)
parent572fc8b5d78310d86cda493b241bc17714f7d44b (diff)
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.
-rw-r--r--src/apprt/gtk/App.zig11
-rw-r--r--src/apprt/gtk/ImguiWidget.zig3
-rw-r--r--src/apprt/gtk/Surface.zig822
-rw-r--r--src/apprt/gtk/Tab.zig8
-rw-r--r--src/apprt/gtk/Window.zig27
-rw-r--r--src/apprt/gtk/key.zig84
-rw-r--r--src/apprt/gtk/winproto.zig7
-rw-r--r--src/apprt/gtk/winproto/noop.zig7
-rw-r--r--src/apprt/gtk/winproto/wayland.zig6
-rw-r--r--src/apprt/gtk/winproto/x11.zig7
-rw-r--r--src/build/SharedDeps.zig6
11 files changed, 567 insertions, 421 deletions
diff --git a/src/apprt/gtk/App.zig b/src/apprt/gtk/App.zig
index 2ed90eccf..cc33fd5e4 100644
--- a/src/apprt/gtk/App.zig
+++ b/src/apprt/gtk/App.zig
@@ -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,
diff --git a/src/apprt/gtk/ImguiWidget.zig b/src/apprt/gtk/ImguiWidget.zig
index 1f42f0b49..735629341 100644
--- a/src/apprt/gtk/ImguiWidget.zig
+++ b/src/apprt/gtk/ImguiWidget.zig
@@ -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);
diff --git a/src/apprt/gtk/Surface.zig b/src/apprt/gtk/Surface.zig
index 805681941..e96b25c16 100644
--- a/src/apprt/gtk/Surface.zig
+++ b/src/apprt/gtk/Surface.zig
@@ -7,6 +7,8 @@ const std = @import("std");
const adw = @import("adw");
const gtk = @import("gtk");
+const gdk = @import("gdk");
+const glib = @import("glib");
const gio = @import("gio");
const gobject = @import("gobject");
@@ -34,7 +36,6 @@ const URLWidget = @import("URLWidget.zig");
const CloseDialog = @import("CloseDialog.zig");
const inspectorpkg = @import("inspector.zig");
const gtk_key = @import("key.zig");
-const c = @import("c.zig").c;
const Builder = @import("Builder.zig");
const adwaita = @import("adwaita.zig");
@@ -85,10 +86,10 @@ pub const Container = union(enum) {
/// Returns the GTK widget to add to the paned for the given
/// element
- pub fn widget(self: Elem) *c.GtkWidget {
+ pub fn widget(self: Elem) *gtk.Widget {
return switch (self) {
.surface => |s| s.primaryWidget(),
- .split => |s| @ptrCast(@alignCast(s.paned)),
+ .split => |s| s.paned.as(gtk.Widget),
};
}
@@ -239,10 +240,10 @@ container: Container = .{ .none = {} },
app: *App,
/// The overlay, this is the primary widget
-overlay: *c.GtkOverlay,
+overlay: *gtk.Overlay,
/// Our GTK area
-gl_area: *c.GtkGLArea,
+gl_area: *gtk.GLArea,
/// If non-null this is the widget on the overlay that shows the URL.
url_widget: ?URLWidget = null,
@@ -254,10 +255,10 @@ resize_overlay: ResizeOverlay = undefined,
zoomed_in: bool = false,
/// If non-null this is the widget on the overlay which dims the surface when it is unfocused
-unfocused_widget: ?*c.GtkWidget = null,
+unfocused_widget: ?*gtk.Widget = null,
/// Any active cursor we may have
-cursor: ?*c.GdkCursor = null,
+cursor: ?*gdk.Cursor = null,
/// Our title. The raw value of the title. This will be kept up to date and
/// .title will be updated if we have focus.
@@ -277,7 +278,7 @@ title_from_terminal: ?[:0]const u8 = null,
pwd: ?[:0]const u8 = null,
/// The timer used to delay title updates in order to prevent flickering.
-update_title_timer: ?c.guint = null,
+update_title_timer: ?c_uint = null,
/// The core surface backing this surface
core_surface: CoreSurface,
@@ -294,7 +295,7 @@ inspector: ?*inspectorpkg.Inspector = null,
/// Key input states. See gtkKeyPressed for detailed descriptions.
in_keyevent: IMKeyEvent = .false,
-im_context: *c.GtkIMContext,
+im_context: *gtk.IMMulticontext,
im_composing: bool = false,
im_buf: [128]u8 = undefined,
im_len: u7 = 0,
@@ -364,15 +365,17 @@ pub fn create(alloc: Allocator, app: *App, opts: Options) !*Surface {
}
pub fn init(self: *Surface, app: *App, opts: Options) !void {
- const gl_area = c.gtk_gl_area_new();
+ const gl_area = gtk.GLArea.new();
+ const gl_area_widget = gl_area.as(gtk.Widget);
// Create an overlay so we can layer the GL area with other widgets.
- const overlay = c.gtk_overlay_new();
- c.gtk_overlay_set_child(@ptrCast(overlay), gl_area);
+ const overlay = gtk.Overlay.new();
+ const overlay_widget = overlay.as(gtk.Widget);
+ overlay.setChild(gl_area_widget);
// Overlay is not focusable, but the GL area is.
- c.gtk_widget_set_focusable(@ptrCast(overlay), 0);
- c.gtk_widget_set_focus_on_click(@ptrCast(overlay), 0);
+ overlay_widget.setFocusable(0);
+ overlay_widget.setFocusOnClick(0);
// We grab the floating reference to the primary widget. This allows the
// widget tree to be moved around i.e. between a split, a tab, etc.
@@ -381,75 +384,81 @@ pub fn init(self: *Surface, app: *App, opts: Options) !void {
//
// This is unref'd in the unref() method that's called by the
// self.container through Elem.deinit.
- _ = c.g_object_ref_sink(@ptrCast(overlay));
- errdefer c.g_object_unref(@ptrCast(overlay));
+ _ = overlay.as(gobject.Object).refSink();
+ errdefer overlay.unref();
// We want the gl area to expand to fill the parent container.
- c.gtk_widget_set_hexpand(gl_area, 1);
- c.gtk_widget_set_vexpand(gl_area, 1);
+ gl_area_widget.setHexpand(1);
+ gl_area_widget.setVexpand(1);
// Various other GL properties
- c.gtk_widget_set_cursor_from_name(@ptrCast(gl_area), "text");
- c.gtk_gl_area_set_required_version(@ptrCast(gl_area), 3, 3);
- c.gtk_gl_area_set_has_stencil_buffer(@ptrCast(gl_area), 0);
- c.gtk_gl_area_set_has_depth_buffer(@ptrCast(gl_area), 0);
- c.gtk_gl_area_set_use_es(@ptrCast(gl_area), 0);
+ gl_area_widget.setCursorFromName("text");
+ gl_area.setRequiredVersion(3, 3);
+ gl_area.setHasStencilBuffer(0);
+ gl_area.setHasDepthBuffer(0);
+ gl_area.setUseEs(0);
// Key event controller will tell us about raw keypress events.
- const ec_key = c.gtk_event_controller_key_new();
- errdefer c.g_object_unref(ec_key);
- c.gtk_widget_add_controller(@ptrCast(overlay), ec_key);
- errdefer c.gtk_widget_remove_controller(@ptrCast(overlay), ec_key);
+ const ec_key = gtk.EventControllerKey.new();
+ errdefer ec_key.unref();
+ overlay_widget.addController(ec_key.as(gtk.EventController));
+ errdefer overlay_widget.removeController(ec_key.as(gtk.EventController));
// Focus controller will tell us about focus enter/exit events
- const ec_focus = c.gtk_event_controller_focus_new();
- errdefer c.g_object_unref(ec_focus);
- c.gtk_widget_add_controller(@ptrCast(overlay), ec_focus);
- errdefer c.gtk_widget_remove_controller(@ptrCast(overlay), ec_focus);
+ const ec_focus = gtk.EventControllerFocus.new();
+ errdefer ec_focus.unref();
+ overlay_widget.addController(ec_focus.as(gtk.EventController));
+ errdefer overlay_widget.removeController(ec_focus.as(gtk.EventController));
// Create a second key controller so we can receive the raw
// key-press events BEFORE the input method gets them.
- const ec_key_press = c.gtk_event_controller_key_new();
- errdefer c.g_object_unref(ec_key_press);
- c.gtk_widget_add_controller(@ptrCast(overlay), ec_key_press);
- errdefer c.gtk_widget_remove_controller(@ptrCast(overlay), ec_key_press);
+ const ec_key_press = gtk.EventControllerKey.new();
+ errdefer ec_key_press.unref();
+ overlay_widget.addController(ec_key_press.as(gtk.EventController));
+ errdefer overlay_widget.removeController(ec_key_press.as(gtk.EventController));
// Clicks
- const gesture_click = c.gtk_gesture_click_new();
- errdefer c.g_object_unref(gesture_click);
- c.gtk_gesture_single_set_button(@ptrCast(gesture_click), 0);
- c.gtk_widget_add_controller(@ptrCast(@alignCast(overlay)), @ptrCast(gesture_click));
+ const gesture_click = gtk.GestureClick.new();
+ errdefer gesture_click.unref();
+ gesture_click.as(gtk.GestureSingle).setButton(0);
+ overlay_widget.addController(gesture_click.as(gtk.EventController));
+ errdefer overlay_widget.removeController(gesture_click.as(gtk.EventController));
// Mouse movement
- const ec_motion = c.gtk_event_controller_motion_new();
- errdefer c.g_object_unref(ec_motion);
- c.gtk_widget_add_controller(@ptrCast(@alignCast(overlay)), ec_motion);
+ const ec_motion = gtk.EventControllerMotion.new();
+ errdefer ec_motion.unref();
+ overlay_widget.addController(ec_motion.as(gtk.EventController));
+ errdefer overlay_widget.removeController(ec_motion.as(gtk.EventController));
// Scroll events
- const ec_scroll = c.gtk_event_controller_scroll_new(c.GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES);
- errdefer c.g_object_unref(ec_scroll);
- c.gtk_widget_add_controller(@ptrCast(overlay), ec_scroll);
+ const ec_scroll = gtk.EventControllerScroll.new(.flags_both_axes);
+ errdefer ec_scroll.unref();
+ overlay_widget.addController(ec_scroll.as(gtk.EventController));
+ errdefer overlay_widget.removeController(ec_scroll.as(gtk.EventController));
// The input method context that we use to translate key events into
// characters. This doesn't have an event key controller attached because
// we call it manually from our own key controller.
- const im_context = c.gtk_im_multicontext_new();
- errdefer c.g_object_unref(im_context);
+ const im_context = gtk.IMMulticontext.new();
+ errdefer im_context.unref();
// The GL area has to be focusable so that it can receive events
- c.gtk_widget_set_focusable(gl_area, 1);
- c.gtk_widget_set_focus_on_click(gl_area, 1);
+ gl_area_widget.setFocusable(1);
+ gl_area_widget.setFocusOnClick(1);
// Set up to handle items being dropped on our surface. Files can be dropped
// from Nautilus and strings can be dropped from many programs.
- const drop_target = c.gtk_drop_target_new(c.G_TYPE_INVALID, c.GDK_ACTION_COPY);
- errdefer c.g_object_unref(drop_target);
- var drop_target_types = [_]c.GType{
- c.gdk_file_list_get_type(),
- c.G_TYPE_STRING,
+ const drop_target = gtk.DropTarget.new(gobject.ext.types.invalid, .flags_copy);
+ errdefer drop_target.unref();
+ // The order of the types matters.
+ var drop_target_types = [_]gobject.Type{
+ gdk.FileList.getGObjectType(),
+ gio.File.getGObjectType(),
+ gobject.ext.types.string,
};
- c.gtk_drop_target_set_gtypes(drop_target, @ptrCast(&drop_target_types), drop_target_types.len);
- c.gtk_widget_add_controller(@ptrCast(overlay), @ptrCast(drop_target));
+ drop_target.setGtypes(&drop_target_types, drop_target_types.len);
+ overlay_widget.addController(drop_target.as(gtk.EventController));
+ errdefer overlay_widget.removeController(drop_target.as(gtk.EventController));
// Inherit the parent's font size if we have a parent.
const font_size: ?font.face.DesiredSize = font_size: {
@@ -496,8 +505,8 @@ pub fn init(self: *Surface, app: *App, opts: Options) !void {
self.* = .{
.app = app,
.container = .{ .none = {} },
- .overlay = @ptrCast(overlay),
- .gl_area = @ptrCast(gl_area),
+ .overlay = overlay,
+ .gl_area = gl_area,
.resize_overlay = undefined,
.title_text = null,
.core_surface = undefined,
@@ -522,28 +531,153 @@ pub fn init(self: *Surface, app: *App, opts: Options) !void {
try self.setMouseShape(.text);
// GL events
- _ = c.g_signal_connect_data(gl_area, "realize", c.G_CALLBACK(&gtkRealize), self, null, c.G_CONNECT_DEFAULT);
- _ = c.g_signal_connect_data(gl_area, "unrealize", c.G_CALLBACK(&gtkUnrealize), self, null, c.G_CONNECT_DEFAULT);
- _ = c.g_signal_connect_data(gl_area, "destroy", c.G_CALLBACK(&gtkDestroy), self, null, c.G_CONNECT_DEFAULT);
- _ = c.g_signal_connect_data(gl_area, "render", c.G_CALLBACK(&gtkRender), self, null, c.G_CONNECT_DEFAULT);
- _ = c.g_signal_connect_data(gl_area, "resize", c.G_CALLBACK(&gtkResize), self, null, c.G_CONNECT_DEFAULT);
-
- _ = c.g_signal_connect_data(ec_key_press, "key-pressed", c.G_CALLBACK(&gtkKeyPressed), self, null, c.G_CONNECT_DEFAULT);
- _ = c.g_signal_connect_data(ec_key_press, "key-released", c.G_CALLBACK(&gtkKeyReleased), self, null, c.G_CONNECT_DEFAULT);
- _ = 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);
- _ = 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(ec_motion, "motion", c.G_CALLBACK(&gtkMouseMotion), self, null, c.G_CONNECT_DEFAULT);
- _ = c.g_signal_connect_data(ec_motion, "leave", c.G_CALLBACK(&gtkMouseLeave), self, null, c.G_CONNECT_DEFAULT);
- _ = c.g_signal_connect_data(ec_scroll, "scroll", c.G_CALLBACK(&gtkMouseScroll), self, null, c.G_CONNECT_DEFAULT);
- _ = c.g_signal_connect_data(ec_scroll, "scroll-begin", c.G_CALLBACK(&gtkMouseScrollPrecisionBegin), self, null, c.G_CONNECT_DEFAULT);
- _ = c.g_signal_connect_data(ec_scroll, "scroll-end", c.G_CALLBACK(&gtkMouseScrollPrecisionEnd), self, null, c.G_CONNECT_DEFAULT);
- _ = c.g_signal_connect_data(im_context, "preedit-start", c.G_CALLBACK(&gtkInputPreeditStart), self, null, c.G_CONNECT_DEFAULT);
- _ = c.g_signal_connect_data(im_context, "preedit-changed", c.G_CALLBACK(&gtkInputPreeditChanged), self, null, c.G_CONNECT_DEFAULT);
- _ = c.g_signal_connect_data(im_context, "preedit-end", c.G_CALLBACK(&gtkInputPreeditEnd), self, null, c.G_CONNECT_DEFAULT);
- _ = c.g_signal_connect_data(im_context, "commit", c.G_CALLBACK(&gtkInputCommit), self, null, c.G_CONNECT_DEFAULT);
- _ = c.g_signal_connect_data(drop_target, "drop", c.G_CALLBACK(&gtkDrop), self, null, c.G_CONNECT_DEFAULT);
+ _ = gtk.Widget.signals.realize.connect(
+ gl_area,
+ *Surface,
+ gtkRealize,
+ self,
+ .{},
+ );
+ _ = gtk.Widget.signals.unrealize.connect(
+ gl_area,
+ *Surface,
+ gtkUnrealize,
+ self,
+ .{},
+ );
+ _ = gtk.Widget.signals.destroy.connect(
+ gl_area,
+ *Surface,
+ gtkDestroy,
+ self,
+ .{},
+ );
+ _ = gtk.GLArea.signals.render.connect(
+ gl_area,
+ *Surface,
+ gtkRender,
+ self,
+ .{},
+ );
+ _ = gtk.GLArea.signals.resize.connect(
+ gl_area,
+ *Surface,
+ gtkResize,
+ self,
+ .{},
+ );
+ _ = gtk.EventControllerKey.signals.key_pressed.connect(
+ ec_key_press,
+ *Surface,
+ gtkKeyPressed,
+ self,
+ .{},
+ );
+ _ = gtk.EventControllerKey.signals.key_released.connect(
+ ec_key_press,
+ *Surface,
+ gtkKeyReleased,
+ self,
+ .{},
+ );
+ _ = gtk.EventControllerFocus.signals.enter.connect(
+ ec_focus,
+ *Surface,
+ gtkFocusEnter,
+ self,
+ .{},
+ );
+ _ = gtk.EventControllerFocus.signals.leave.connect(
+ ec_focus,
+ *Surface,
+ gtkFocusLeave,
+ self,
+ .{},
+ );
+ _ = gtk.GestureClick.signals.pressed.connect(
+ gesture_click,
+ *Surface,
+ gtkMouseDown,
+ self,
+ .{},
+ );
+ _ = gtk.GestureClick.signals.released.connect(
+ gesture_click,
+ *Surface,
+ gtkMouseUp,
+ self,
+ .{},
+ );
+ _ = gtk.EventControllerMotion.signals.motion.connect(
+ ec_motion,
+ *Surface,
+ gtkMouseMotion,
+ self,
+ .{},
+ );
+ _ = gtk.EventControllerMotion.signals.leave.connect(
+ ec_motion,
+ *Surface,
+ gtkMouseLeave,
+ self,
+ .{},
+ );
+ _ = gtk.EventControllerScroll.signals.scroll.connect(
+ ec_scroll,
+ *Surface,
+ gtkMouseScroll,
+ self,
+ .{},
+ );
+ _ = gtk.EventControllerScroll.signals.scroll_begin.connect(
+ ec_scroll,
+ *Surface,
+ gtkMouseScrollPrecisionBegin,
+ self,
+ .{},
+ );
+ _ = gtk.EventControllerScroll.signals.scroll_end.connect(
+ ec_scroll,
+ *Surface,
+ gtkMouseScrollPrecisionEnd,
+ self,
+ .{},
+ );
+ _ = gtk.IMContext.signals.preedit_start.connect(
+ im_context,
+ *Surface,
+ gtkInputPreeditStart,
+ self,
+ .{},
+ );
+ _ = gtk.IMContext.signals.preedit_changed.connect(
+ im_context,
+ *Surface,
+ gtkInputPreeditChanged,
+ self,
+ .{},
+ );
+ _ = gtk.IMContext.signals.preedit_end.connect(
+ im_context,
+ *Surface,
+ gtkInputPreeditEnd,
+ self,
+ .{},
+ );
+ _ = gtk.IMContext.signals.commit.connect(
+ im_context,
+ *Surface,
+ gtkInputCommit,
+ self,
+ .{},
+ );
+ _ = gtk.DropTarget.signals.drop.connect(
+ drop_target,
+ *Surface,
+ gtkDrop,
+ self,
+ .{},
+ );
}
fn realize(self: *Surface) !void {
@@ -618,9 +752,9 @@ pub fn deinit(self: *Surface) void {
// Note we don't do anything with the "unfocused_overlay" because
// it is attached to the overlay which by this point has been destroyed
// and therefore the unfocused_overlay has been destroyed as well.
- c.g_object_unref(self.im_context);
- if (self.cursor) |cursor| c.g_object_unref(cursor);
- if (self.update_title_timer) |timer| _ = c.g_source_remove(timer);
+ self.im_context.unref();
+ if (self.cursor) |cursor| cursor.unref();
+ if (self.update_title_timer) |timer| _ = glib.Source.remove(timer);
self.resize_overlay.deinit();
}
@@ -632,7 +766,7 @@ pub fn updateConfig(self: *Surface, config: *const configpkg.Config) !void {
// unref removes the long-held reference to the gl_area and kicks off the
// deinit/destroy process for this surface.
pub fn unref(self: *Surface) void {
- c.g_object_unref(self.overlay);
+ self.overlay.unref();
}
pub fn destroy(self: *Surface, alloc: Allocator) void {
@@ -640,8 +774,8 @@ pub fn destroy(self: *Surface, alloc: Allocator) void {
alloc.destroy(self);
}
-pub fn primaryWidget(self: *Surface) *c.GtkWidget {
- return @ptrCast(@alignCast(self.overlay));
+pub fn primaryWidget(self: *Surface) *gtk.Widget {
+ return self.overlay.as(gtk.Widget);
}
fn render(self: *Surface) !void {
@@ -660,7 +794,7 @@ pub fn queueInspectorRender(self: *Surface) void {
/// Invalidate the surface so that it forces a redraw on the next tick.
pub fn redraw(self: *Surface) void {
- c.gtk_gl_area_queue_render(self.gl_area);
+ self.gl_area.queueRender();
}
/// Close this surface.
@@ -712,16 +846,6 @@ pub fn controlInspector(
};
}
-pub fn getTitleLabel(self: *Surface) ?*c.GtkWidget {
- switch (self.title) {
- .none => return null,
- .label => |label| {
- const widget = @as(*c.GtkWidget, @ptrCast(@alignCast(label)));
- return widget;
- },
- }
-}
-
pub fn setShouldClose(self: *Surface) void {
_ = self;
}
@@ -733,7 +857,7 @@ pub fn shouldClose(self: *const Surface) bool {
pub fn getContentScale(self: *const Surface) !apprt.ContentScale {
const gtk_scale: f32 = scale: {
- const widget: *gtk.Widget = @ptrCast(@alignCast(self.gl_area));
+ const widget = self.gl_area.as(gtk.Widget);
// Future: detect GTK version 4.12+ and use gdk_surface_get_scale so we
// can support fractional scaling.
const scale = widget.getScaleFactor();
@@ -796,13 +920,12 @@ pub fn setInitialWindowSize(self: *const Surface, width: u32, height: u32) !void
const window = self.container.window() orelse return;
if (window.notebook.nPages() > 1) return;
+ // FIXME: when Window is converted to zig-gobject
+ const gtk_window: *gtk.Window = @ptrCast(@alignCast(window.window));
+
// Note: this doesn't properly take into account the window decorations.
// I'm not currently sure how to do that.
- c.gtk_window_set_default_size(
- @ptrCast(window.window),
- @intCast(width),
- @intCast(height),
- );
+ gtk_window.setDefaultSize(@intCast(width), @intCast(height));
}
pub fn setSizeLimits(self: *const Surface, min: apprt.SurfaceSize, max_: ?apprt.SurfaceSize) !void {
@@ -818,13 +941,12 @@ pub fn setSizeLimits(self: *const Surface, min: apprt.SurfaceSize, max_: ?apprt.
const window = self.container.window() orelse return;
if (window.notebook.nPages() > 1) return;
+ // FIXME: when Window is converted to zig-gobject
+ const widget: *gtk.Widget = @ptrCast(@alignCast(window.window));
+
// Note: this doesn't properly take into account the window decorations.
// I'm not currently sure how to do that.
- c.gtk_widget_set_size_request(
- @ptrCast(window.window),
- @intCast(min.width),
- @intCast(min.height),
- );
+ widget.setSizeRequest(@intCast(min.width), @intCast(min.height));
}
pub fn grabFocus(self: *Surface) void {
@@ -839,8 +961,7 @@ pub fn grabFocus(self: *Surface) void {
tab.focus_child = self;
}
- const widget = @as(*c.GtkWidget, @ptrCast(self.gl_area));
- _ = c.gtk_widget_grab_focus(widget);
+ _ = self.gl_area.as(gtk.Widget).grabFocus();
self.updateTitleLabels();
}
@@ -856,8 +977,8 @@ fn updateTitleLabels(self: *Surface) void {
// If we have a window and are focused, then we have to update the window title.
if (self.container.window()) |window| {
- const widget = @as(*c.GtkWidget, @ptrCast(self.gl_area));
- if (c.gtk_widget_is_focus(widget) == 1) {
+ const widget = self.gl_area.as(gtk.Widget);
+ if (widget.isFocus() != 0) {
// Changing the title somehow unhides our cursor.
// https://github.com/ghostty-org/ghostty/issues/1419
// I don't know a way around this yet. I've tried re-hiding the
@@ -897,21 +1018,21 @@ pub fn setTitle(self: *Surface, slice: [:0]const u8, source: SetTitleSource) !vo
// delay the title update to prevent flickering
if (self.update_title_timer) |timer| {
- if (c.g_source_remove(timer) == c.FALSE) {
+ if (glib.Source.remove(timer) == 0) {
log.warn("unable to remove update title timer", .{});
}
self.update_title_timer = null;
}
- self.update_title_timer = c.g_timeout_add(75, updateTitleTimerExpired, self);
+ self.update_title_timer = glib.timeoutAdd(75, updateTitleTimerExpired, self);
}
-fn updateTitleTimerExpired(ctx: ?*anyopaque) callconv(.C) c.gboolean {
- const self: *Surface = @ptrCast(@alignCast(ctx));
+fn updateTitleTimerExpired(ud: ?*anyopaque) callconv(.C) c_int {
+ const self: *Surface = @ptrCast(@alignCast(ud.?));
self.updateTitleLabels();
self.update_title_timer = null;
- return c.FALSE;
+ return 0;
}
pub fn getTitle(self: *Surface) ?[:0]const u8 {
@@ -1015,22 +1136,22 @@ pub fn setMouseShape(
.zoom_out => "zoom-out",
};
- const cursor = c.gdk_cursor_new_from_name(name.ptr, null) orelse {
+ const cursor = gdk.Cursor.newFromName(name.ptr, null) orelse {
log.warn("unsupported cursor name={s}", .{name});
return;
};
- errdefer c.g_object_unref(cursor);
+ errdefer cursor.unref();
// Set our new cursor. We only do this if the cursor we currently
// have is NOT set to "none" because setting the cursor causes it
// to become visible again.
- const gl_area_widget: *c.GtkWidget = @ptrCast(@alignCast(self.gl_area));
- if (c.gtk_widget_get_cursor(gl_area_widget) != self.app.cursor_none) {
- c.gtk_widget_set_cursor(gl_area_widget, cursor);
+ const widget = self.gl_area.as(gtk.Widget);
+ if (widget.getCursor() != self.app.cursor_none) {
+ widget.setCursor(cursor);
}
// Free our existing cursor
- if (self.cursor) |old| c.g_object_unref(old);
+ if (self.cursor) |old| old.unref();
self.cursor = cursor;
}
@@ -1039,22 +1160,22 @@ pub fn setMouseVisibility(self: *Surface, visible: bool) void {
// Note in there that self.cursor or cursor_none may be null. That's
// not a problem because NULL is a valid argument for set cursor
// which means to just use the parent value.
- const gl_area_widget: *c.GtkWidget = @ptrCast(@alignCast(self.gl_area));
+ const widget = self.gl_area.as(gtk.Widget);
if (visible) {
- c.gtk_widget_set_cursor(gl_area_widget, self.cursor);
+ widget.setCursor(self.cursor);
return;
}
+ // FIXME: when App is converted to zig-gobject
// Set our new cursor to the app "none" cursor
- c.gtk_widget_set_cursor(gl_area_widget, self.app.cursor_none);
+ widget.setCursor(@ptrCast(@alignCast(self.app.cursor_none)));
}
pub fn mouseOverLink(self: *Surface, uri_: ?[]const u8) void {
const uri = uri_ orelse {
if (self.url_widget) |*widget| {
- // FIXME: when Surface gets converted to zig-gobject
- widget.deinit(@ptrCast(@alignCast(self.overlay)));
+ widget.deinit(self.overlay);
self.url_widget = null;
}
@@ -1072,8 +1193,7 @@ pub fn mouseOverLink(self: *Surface, uri_: ?[]const u8) void {
return;
}
- // FIXME: when Surface gets converted to zig-gobject
- self.url_widget = URLWidget.init(@ptrCast(@alignCast(self.overlay)), uriZ);
+ self.url_widget = URLWidget.init(self.overlay, uriZ);
}
pub fn supportsClipboard(
@@ -1102,13 +1222,9 @@ pub fn clipboardRequest(
ud_ptr.* = .{ .self = self, .state = state };
// Start our async request
- const clipboard = getClipboard(@ptrCast(self.gl_area), clipboard_type);
- c.gdk_clipboard_read_text_async(
- clipboard,
- null,
- &gtkClipboardRead,
- ud_ptr,
- );
+ const clipboard = getClipboard(@ptrCast(self.gl_area), clipboard_type) orelse return;
+
+ clipboard.readTextAsync(null, gtkClipboardRead, ud_ptr);
}
pub fn setClipboardString(
@@ -1118,8 +1234,9 @@ pub fn setClipboardString(
confirm: bool,
) !void {
if (!confirm) {
- const clipboard = getClipboard(@ptrCast(self.gl_area), clipboard_type);
- c.gdk_clipboard_set_text(clipboard, val.ptr);
+ const clipboard = getClipboard(@ptrCast(self.gl_area), clipboard_type) orelse return;
+ clipboard.setText(val);
+
// We only toast if we are copying to the standard clipboard.
if (clipboard_type == .standard and
self.app.config.@"app-notifications".@"clipboard-copy")
@@ -1147,27 +1264,25 @@ const ClipboardRequest = struct {
};
fn gtkClipboardRead(
- source: ?*c.GObject,
- res: ?*c.GAsyncResult,
+ source: ?*gobject.Object,
+ res: *gio.AsyncResult,
ud: ?*anyopaque,
) callconv(.C) void {
+ const clipboard = gobject.ext.cast(gdk.Clipboard, source orelse return) orelse return;
const req: *ClipboardRequest = @ptrCast(@alignCast(ud orelse return));
const self = req.self;
const alloc = self.app.core_app.alloc;
defer alloc.destroy(req);
- var gerr: ?*c.GError = null;
- const cstr = c.gdk_clipboard_read_text_finish(
- @ptrCast(source orelse return),
- res,
- &gerr,
- );
+ var gerr: ?*glib.Error = null;
+ const cstr_ = clipboard.readTextFinish(res, &gerr);
if (gerr) |err| {
- defer c.g_error_free(err);
- log.warn("failed to read clipboard err={s}", .{err.message});
+ defer err.free();
+ log.warn("failed to read clipboard err={s}", .{err.f_message orelse "(no message)"});
return;
}
- defer c.g_free(cstr);
+ const cstr = cstr_ orelse return;
+ defer glib.free(cstr);
const str = std.mem.sliceTo(cstr, 0);
self.core_surface.completeClipboardRequest(
@@ -1195,10 +1310,10 @@ fn gtkClipboardRead(
};
}
-fn getClipboard(widget: *c.GtkWidget, clipboard: apprt.Clipboard) ?*c.GdkClipboard {
+fn getClipboard(widget: *gtk.Widget, clipboard: apprt.Clipboard) ?*gdk.Clipboard {
return switch (clipboard) {
- .standard => c.gtk_widget_get_clipboard(widget),
- .selection, .primary => c.gtk_widget_get_primary_clipboard(widget),
+ .standard => widget.getClipboard(),
+ .selection, .primary => widget.getPrimaryClipboard(),
};
}
@@ -1217,35 +1332,33 @@ pub fn showDesktopNotification(
else => title,
};
- const notification = c.g_notification_new(t.ptr);
- defer c.g_object_unref(notification);
- c.g_notification_set_body(notification, body.ptr);
+ const notification = gio.Notification.new(t);
+ defer notification.unref();
+ notification.setBody(body);
- const icon = c.g_themed_icon_new(build_config.bundle_id);
- defer c.g_object_unref(icon);
- c.g_notification_set_icon(notification, icon);
+ const icon = gio.ThemedIcon.new(build_config.bundle_id);
+ defer icon.unref();
- const pointer = c.g_variant_new_uint64(@intFromPtr(&self.core_surface));
- c.g_notification_set_default_action_and_target_value(
- notification,
- "app.present-surface",
- pointer,
- );
+ notification.setIcon(icon);
- const g_app: *c.GApplication = @ptrCast(self.app.app);
+ const pointer = glib.Variant.newUint64(@intFromPtr(&self.core_surface));
+ notification.setDefaultActionAndTargetValue("app.present-surface", pointer);
+
+ // FIXME: when App.zig gets converted to zig-gobject
+ const app: gio.Application = @ptrCast(@alignCast(self.app.app));
// We set the notification ID to the body content. If the content is the
// same, this notification may replace a previous notification
- c.g_application_send_notification(g_app, body.ptr, notification);
+ app.sendNotification(body.ptr, notification);
}
-fn gtkRealize(area: *c.GtkGLArea, ud: ?*anyopaque) callconv(.C) void {
+fn gtkRealize(gl_area: *gtk.GLArea, self: *Surface) callconv(.C) void {
log.debug("gl surface realized", .{});
// We need to make the context current so we can call GL functions.
- c.gtk_gl_area_make_current(area);
- if (c.gtk_gl_area_get_error(area)) |err| {
- log.err("surface failed to realize: {s}", .{err.*.message});
+ gl_area.makeCurrent();
+ if (gl_area.getError()) |err| {
+ log.err("surface failed to realize: {s}", .{err.f_message orelse "(no message)"});
log.warn("this error is usually due to a driver or gtk bug", .{});
log.warn("this is a common cause of this issue: https://gitlab.gnome.org/GNOME/gtk/-/issues/4950", .{});
return;
@@ -1253,7 +1366,6 @@ fn gtkRealize(area: *c.GtkGLArea, ud: ?*anyopaque) callconv(.C) void {
// realize means that our OpenGL context is ready, so we can now
// initialize the core surface which will setup the renderer.
- const self = userdataSelf(ud.?);
self.realize() catch |err| {
// TODO: we need to destroy the GL area here.
log.err("surface failed to realize: {}", .{err});
@@ -1263,28 +1375,21 @@ fn gtkRealize(area: *c.GtkGLArea, ud: ?*anyopaque) callconv(.C) void {
// When we have a realized surface, we also attach our input method context.
// We do this here instead of init because this allows us to release the ref
// to the GLArea when we unrealized.
- c.gtk_im_context_set_client_widget(self.im_context, @ptrCast(@alignCast(self.overlay)));
+ self.im_context.as(gtk.IMContext).setClientWidget(self.overlay.as(gtk.Widget));
}
/// This is called when the underlying OpenGL resources must be released.
/// This is usually due to the OpenGL area changing GDK surfaces.
-fn gtkUnrealize(area: *c.GtkGLArea, ud: ?*anyopaque) callconv(.C) void {
- _ = area;
-
+fn gtkUnrealize(_: *gtk.GLArea, self: *Surface) callconv(.C) void {
log.debug("gl surface unrealized", .{});
- const self = userdataSelf(ud.?);
self.core_surface.renderer.displayUnrealized();
// See gtkRealize for why we do this here.
- c.gtk_im_context_set_client_widget(self.im_context, null);
+ self.im_context.as(gtk.IMContext).setClientWidget(null);
}
/// render signal
-fn gtkRender(area: *c.GtkGLArea, ctx: *c.GdkGLContext, ud: ?*anyopaque) callconv(.C) c.gboolean {
- _ = area;
- _ = ctx;
-
- const self = userdataSelf(ud.?);
+fn gtkRender(_: *gtk.GLArea, _: *gdk.GLContext, self: *Surface) callconv(.C) c_int {
self.render() catch |err| {
log.err("surface failed to render: {}", .{err});
return 0;
@@ -1293,21 +1398,22 @@ fn gtkRender(area: *c.GtkGLArea, ctx: *c.GdkGLContext, ud: ?*anyopaque) callconv
return 1;
}
-/// render signal
-fn gtkResize(area: *c.GtkGLArea, width: c.gint, height: c.gint, ud: ?*anyopaque) callconv(.C) void {
- const self = userdataSelf(ud.?);
-
+/// resize signal
+fn gtkResize(gl_area: *gtk.GLArea, width: c_int, height: c_int, self: *Surface) callconv(.C) void {
// Some debug output to help understand what GTK is telling us.
{
const scale_factor = scale: {
- const widget = @as(*c.GtkWidget, @ptrCast(area));
- break :scale c.gtk_widget_get_scale_factor(widget);
+ const widget = gl_area.as(gtk.Widget);
+ break :scale widget.getScaleFactor();
};
const window_scale_factor = scale: {
const window = self.container.window() orelse break :scale 0;
- const gdk_surface = c.gtk_native_get_surface(@ptrCast(window.window));
- break :scale c.gdk_surface_get_scale_factor(gdk_surface);
+ // FIXME: when Window.zig is converted to zig-gobject
+ const gtk_window: *gtk.Window = @ptrCast(@alignCast(window.window));
+ const gtk_native = gtk_window.as(gtk.Native);
+ const gdk_surface = gtk_native.getSurface() orelse break :scale 0;
+ break :scale gdk_surface.getScaleFactor();
};
log.debug("gl resize width={} height={} scale={} window_scale={}", .{
@@ -1350,11 +1456,9 @@ fn gtkResize(area: *c.GtkGLArea, width: c.gint, height: c.gint, ud: ?*anyopaque)
}
/// "destroy" signal for surface
-fn gtkDestroy(v: *c.GtkWidget, ud: ?*anyopaque) callconv(.C) void {
- _ = v;
+fn gtkDestroy(_: *gtk.GLArea, self: *Surface) callconv(.C) void {
log.debug("gl destroy", .{});
- const self = userdataSelf(ud.?);
const alloc = self.app.core_app.alloc;
self.deinit();
alloc.destroy(self);
@@ -1363,14 +1467,15 @@ fn gtkDestroy(v: *c.GtkWidget, ud: ?*anyopaque) callconv(.C) void {
/// Scale x/y by the GDK device scale.
fn scaledCoordinates(
self: *const Surface,
- x: c.gdouble,
- y: c.gdouble,
+ x: f64,
+ y: f64,
) struct {
- x: c.gdouble,
- y: c.gdouble,
+ x: f64,
+ y: f64,
} {
+ const gl_are_widget = self.gl_area.as(gtk.Widget);
const scale_factor: f64 = @floatFromInt(
- c.gtk_widget_get_scale_factor(@ptrCast(self.gl_area)),
+ gl_are_widget.getScaleFactor(),
);
return .{
@@ -1380,23 +1485,22 @@ fn scaledCoordinates(
}
fn gtkMouseDown(
- gesture: *c.GtkGestureClick,
- _: c.gint,
- x: c.gdouble,
- y: c.gdouble,
- ud: ?*anyopaque,
+ gesture: *gtk.GestureClick,
+ _: c_int,
+ x: f64,
+ y: f64,
+ self: *Surface,
) callconv(.C) void {
- const event = c.gtk_event_controller_get_current_event(@ptrCast(gesture)) orelse return;
+ const event = gesture.as(gtk.EventController).getCurrentEvent() orelse return;
- const self = userdataSelf(ud.?);
- const gtk_mods = c.gdk_event_get_modifier_state(event);
+ const gtk_mods = event.getModifierState();
- const button = translateMouseButton(c.gtk_gesture_single_get_current_button(@ptrCast(gesture)));
+ const button = translateMouseButton(gesture.as(gtk.GestureSingle).getCurrentButton());
const mods = gtk_key.translateMods(gtk_mods);
// If we don't have focus, grab it.
- const gl_widget = @as(*c.GtkWidget, @ptrCast(self.gl_area));
- if (c.gtk_widget_has_focus(gl_widget) == 0) {
+ const gl_area_widget = self.gl_area.as(gtk.Widget);
+ if (gl_area_widget.hasFocus() == 0) {
self.grabFocus();
}
@@ -1414,20 +1518,19 @@ fn gtkMouseDown(
}
fn gtkMouseUp(
- gesture: *c.GtkGestureClick,
- _: c.gint,
- _: c.gdouble,
- _: c.gdouble,
- ud: ?*anyopaque,
+ gesture: *gtk.GestureClick,
+ _: c_int,
+ _: f64,
+ _: f64,
+ self: *Surface,
) callconv(.C) void {
- const event = c.gtk_event_controller_get_current_event(@ptrCast(gesture)) orelse return;
+ const event = gesture.as(gtk.EventController).getCurrentEvent() orelse return;
- const gtk_mods = c.gdk_event_get_modifier_state(event);
+ const gtk_mods = event.getModifierState();
- const button = translateMouseButton(c.gtk_gesture_single_get_current_button(@ptrCast(gesture)));
+ const button = translateMouseButton(gesture.as(gtk.GestureSingle).getCurrentButton());
const mods = gtk_key.translateMods(gtk_mods);
- const self = userdataSelf(ud.?);
_ = self.core_surface.mouseButtonCallback(.release, button, mods) catch |err| {
log.err("error in key callback err={}", .{err});
return;
@@ -1435,14 +1538,13 @@ fn gtkMouseUp(
}
fn gtkMouseMotion(
- ec: *c.GtkEventControllerMotion,
- x: c.gdouble,
- y: c.gdouble,
- ud: ?*anyopaque,
+ ec: *gtk.EventControllerMotion,
+ x: f64,
+ y: f64,
+ self: *Surface,
) callconv(.C) void {
- const event = c.gtk_event_controller_get_current_event(@ptrCast(ec)) orelse return;
+ const event = ec.as(gtk.EventController).getCurrentEvent() orelse return;
- const self = userdataSelf(ud.?);
const scaled = self.scaledCoordinates(x, y);
const pos: apprt.CursorPos = .{
@@ -1464,8 +1566,8 @@ fn gtkMouseMotion(
if (!is_cursor_still) {
// If we don't have focus, and we want it, grab it.
- const gl_widget = @as(*c.GtkWidget, @ptrCast(self.gl_area));
- if (c.gtk_widget_has_focus(gl_widget) == 0 and self.app.config.@"focus-follows-mouse") {
+ const gl_area_widget = self.gl_area.as(gtk.Widget);
+ if (gl_area_widget.hasFocus() == 0 and self.app.config.@"focus-follows-mouse") {
self.grabFocus();
}
@@ -1473,7 +1575,7 @@ fn gtkMouseMotion(
self.cursor_pos = pos;
// Get our modifiers
- const gtk_mods = c.gdk_event_get_modifier_state(event);
+ const gtk_mods = event.getModifierState();
const mods = gtk_key.translateMods(gtk_mods);
self.core_surface.cursorPosCallback(self.cursor_pos, mods) catch |err| {
@@ -1484,15 +1586,13 @@ fn gtkMouseMotion(
}
fn gtkMouseLeave(
- ec: *c.GtkEventControllerMotion,
- ud: ?*anyopaque,
+ ec_motion: *gtk.EventControllerMotion,
+ self: *Surface,
) callconv(.C) void {
- const event = c.gtk_event_controller_get_current_event(@ptrCast(ec)) orelse return;
-
- const self = userdataSelf(ud.?);
+ const event = ec_motion.as(gtk.EventController).getCurrentEvent() orelse return;
// Get our modifiers
- const gtk_mods = c.gdk_event_get_modifier_state(event);
+ const gtk_mods = event.getModifierState();
const mods = gtk_key.translateMods(gtk_mods);
self.core_surface.cursorPosCallback(.{ .x = -1, .y = -1 }, mods) catch |err| {
log.err("error in cursor pos callback err={}", .{err});
@@ -1501,34 +1601,31 @@ fn gtkMouseLeave(
}
fn gtkMouseScrollPrecisionBegin(
- _: *c.GtkEventControllerScroll,
- ud: ?*anyopaque,
+ _: *gtk.EventControllerScroll,
+ self: *Surface,
) callconv(.C) void {
- const self = userdataSelf(ud.?);
self.precision_scroll = true;
}
fn gtkMouseScrollPrecisionEnd(
- _: *c.GtkEventControllerScroll,
- ud: ?*anyopaque,
+ _: *gtk.EventControllerScroll,
+ self: *Surface,
) callconv(.C) void {
- const self = userdataSelf(ud.?);
self.precision_scroll = false;
}
fn gtkMouseScroll(
- _: *c.GtkEventControllerScroll,
- x: c.gdouble,
- y: c.gdouble,
- ud: ?*anyopaque,
-) callconv(.C) void {
- const self = userdataSelf(ud.?);
+ _: *gtk.EventControllerScroll,
+ x: f64,
+ y: f64,
+ self: *Surface,
+) callconv(.C) c_int {
const scaled = self.scaledCoordinates(x, y);
// GTK doesn't support any of the scroll mods.
const scroll_mods: input.ScrollMods = .{ .precision = self.precision_scroll };
// Multiply precision scrolls by 10 to get a better response from touchpad scrolling
- const multiplier: f64 = if (self.precision_scroll) 10 else 1;
+ const multiplier: f64 = if (self.precision_scroll) 10.0 else 1.0;
self.core_surface.scrollCallback(
// We invert because we apply natural scrolling to the values.
@@ -1540,42 +1637,42 @@ fn gtkMouseScroll(
scroll_mods,
) catch |err| {
log.err("error in scroll callback err={}", .{err});
- return;
+ return 0;
};
+
+ return 1;
}
fn gtkKeyPressed(
- ec_key: *c.GtkEventControllerKey,
- keyval: c.guint,
- keycode: c.guint,
- gtk_mods: c.GdkModifierType,
- ud: ?*anyopaque,
-) callconv(.C) c.gboolean {
- const self = userdataSelf(ud.?);
- return if (self.keyEvent(
+ ec_key: *gtk.EventControllerKey,
+ keyval: c_uint,
+ keycode: c_uint,
+ gtk_mods: gdk.ModifierType,
+ self: *Surface,
+) callconv(.C) c_int {
+ return @intFromBool(self.keyEvent(
.press,
ec_key,
keyval,
keycode,
gtk_mods,
- )) 1 else 0;
+ ));
}
fn gtkKeyReleased(
- ec_key: *c.GtkEventControllerKey,
- keyval: c.guint,
- keycode: c.guint,
- state: c.GdkModifierType,
- ud: ?*anyopaque,
-) callconv(.C) c.gboolean {
- const self = userdataSelf(ud.?);
- return if (self.keyEvent(
+ ec_key: *gtk.EventControllerKey,
+ keyval: c_uint,
+ keycode: c_uint,
+ state: gdk.ModifierType,
+ self: *Surface,
+) callconv(.C) void {
+ _ = self.keyEvent(
.release,
ec_key,
keyval,
keycode,
state,
- )) 1 else 0;
+ );
}
/// Key press event (press or release).
@@ -1611,15 +1708,14 @@ fn gtkKeyReleased(
pub fn keyEvent(
self: *Surface,
action: input.Action,
- ec_key: *c.GtkEventControllerKey,
- keyval: c.guint,
- keycode: c.guint,
- gtk_mods: c.GdkModifierType,
+ ec_key: *gtk.EventControllerKey,
+ keyval: c_uint,
+ keycode: c_uint,
+ gtk_mods: gdk.ModifierType,
) bool {
// log.warn("GTKIM: keyEvent action={}", .{action});
- const event = c.gtk_event_controller_get_current_event(
- @ptrCast(ec_key),
- ) orelse return false;
+ const event = ec_key.as(gtk.EventController).getCurrentEvent() orelse return false;
+ const key_event = gobject.ext.cast(gdk.KeyEvent, event) orelse return false;
// The block below is all related to input method handling. See the function
// comment for some high level details and then the comments within
@@ -1629,11 +1725,11 @@ pub fn keyEvent(
// where the cursor is so it can render the dropdowns in the correct
// place.
const ime_point = self.core_surface.imePoint();
- c.gtk_im_context_set_cursor_location(self.im_context, &.{
- .x = @intFromFloat(ime_point.x),
- .y = @intFromFloat(ime_point.y),
- .width = 1,
- .height = 1,
+ self.im_context.as(gtk.IMContext).setCursorLocation(&.{
+ .f_x = @intFromFloat(ime_point.x),
+ .f_y = @intFromFloat(ime_point.y),
+ .f_width = 1,
+ .f_height = 1,
});
// We note that we're in a keypress because we want some logic to
@@ -1669,10 +1765,7 @@ pub fn keyEvent(
// triggered despite being technically consumed. At the time of
// writing, both Kitty and Alacritty have the same behavior. I
// know of no way to fix this.
- const im_handled = c.gtk_im_context_filter_keypress(
- self.im_context,
- event,
- ) != 0;
+ const im_handled = self.im_context.as(gtk.IMContext).filterKeypress(event) != 0;
// log.warn("GTKIM: im_handled={} im_len={} im_composing={}", .{
// im_handled,
// self.im_len,
@@ -1721,10 +1814,10 @@ pub fn keyEvent(
defer self.im_len = 0;
// Get the keyvals for this event.
- const keyval_unicode = c.gdk_keyval_to_unicode(keyval);
+ const keyval_unicode = gdk.keyvalToUnicode(keyval);
const keyval_unicode_unshifted: u21 = gtk_key.keyvalUnicodeUnshifted(
- @ptrCast(self.gl_area),
- event,
+ self.gl_area.as(gtk.Widget),
+ key_event,
keycode,
);
@@ -1745,9 +1838,12 @@ pub fn keyEvent(
// Get our consumed modifiers
const consumed_mods: input.Mods = consumed: {
- const raw = c.gdk_key_event_get_consumed_modifiers(event);
- const masked = raw & c.GDK_MODIFIER_MASK;
- break :consumed gtk_key.translateMods(masked);
+ const T = @typeInfo(gdk.ModifierType);
+ std.debug.assert(T.@"struct".layout == .@"packed");
+ const I = T.@"struct".backing_integer.?;
+
+ const masked = @as(I, @bitCast(key_event.getConsumedModifiers())) & @as(I, gdk.MODIFIER_MASK);
+ break :consumed gtk_key.translateMods(@bitCast(masked));
};
// If we're not in a dead key state, we want to translate our text
@@ -1846,7 +1942,7 @@ pub fn keyEvent(
// because there is other IME state that we want to preserve,
// such as quotation mark ordering for Chinese input.
if (self.im_composing) {
- c.gtk_im_context_reset(self.im_context);
+ self.im_context.as(gtk.IMContext).reset();
self.core_surface.preeditCallback(null) catch {};
}
@@ -1858,11 +1954,10 @@ pub fn keyEvent(
}
fn gtkInputPreeditStart(
- _: *c.GtkIMContext,
- ud: ?*anyopaque,
+ _: *gtk.IMMulticontext,
+ self: *Surface,
) callconv(.C) void {
// log.warn("GTKIM: preedit start", .{});
- const self = userdataSelf(ud.?);
// Start our composing state for the input method and reset our
// input buffer to empty.
@@ -1871,15 +1966,14 @@ fn gtkInputPreeditStart(
}
fn gtkInputPreeditChanged(
- ctx: *c.GtkIMContext,
- ud: ?*anyopaque,
+ ctx: *gtk.IMMulticontext,
+ self: *Surface,
) callconv(.C) void {
- const self = userdataSelf(ud.?);
-
// Get our pre-edit string that we'll use to show the user.
- var buf: [*c]u8 = undefined;
- _ = c.gtk_im_context_get_preedit_string(ctx, &buf, null, null);
- defer c.g_free(buf);
+ var buf: [*:0]u8 = undefined;
+ ctx.as(gtk.IMContext).getPreeditString(&buf, null, null);
+ defer glib.free(buf);
+
const str = std.mem.sliceTo(buf, 0);
// Update our preedit state in Ghostty core
@@ -1890,11 +1984,10 @@ fn gtkInputPreeditChanged(
}
fn gtkInputPreeditEnd(
- _: *c.GtkIMContext,
- ud: ?*anyopaque,
+ _: *gtk.IMMulticontext,
+ self: *Surface,
) callconv(.C) void {
// log.warn("GTKIM: preedit end", .{});
- const self = userdataSelf(ud.?);
// End our composing state for GTK, allowing us to commit the text.
self.im_composing = false;
@@ -1906,11 +1999,10 @@ fn gtkInputPreeditEnd(
}
fn gtkInputCommit(
- _: *c.GtkIMContext,
+ _: *gtk.IMMulticontext,
bytes: [*:0]u8,
- ud: ?*anyopaque,
+ self: *Surface,
) callconv(.C) void {
- const self = userdataSelf(ud.?);
const str = std.mem.sliceTo(bytes, 0);
// log.debug("GTKIM: input commit composing={} keyevent={} str={s}", .{
@@ -1985,16 +2077,15 @@ fn gtkInputCommit(
};
}
-fn gtkFocusEnter(_: *c.GtkEventControllerFocus, ud: ?*anyopaque) callconv(.C) void {
- const self = userdataSelf(ud.?);
+fn gtkFocusEnter(_: *gtk.EventControllerFocus, self: *Surface) callconv(.C) void {
if (!self.realized) return;
// Notify our IM context
- c.gtk_im_context_focus_in(self.im_context);
+ self.im_context.as(gtk.IMContext).focusIn();
// Remove the unfocused widget overlay, if we have one
if (self.unfocused_widget) |widget| {
- c.gtk_overlay_remove_overlay(self.overlay, widget);
+ self.overlay.removeOverlay(widget);
self.unfocused_widget = null;
}
@@ -2011,12 +2102,11 @@ fn gtkFocusEnter(_: *c.GtkEventControllerFocus, ud: ?*anyopaque) callconv(.C) vo
};
}
-fn gtkFocusLeave(_: *c.GtkEventControllerFocus, ud: ?*anyopaque) callconv(.C) void {
- const self = userdataSelf(ud.?);
+fn gtkFocusLeave(_: *gtk.EventControllerFocus, self: *Surface) callconv(.C) void {
if (!self.realized) return;
// Notify our IM context
- c.gtk_im_context_focus_out(self.im_context);
+ self.im_context.as(gtk.IMContext).focusOut();
// We only try dimming the surface if we are a split
switch (self.container) {
@@ -2032,8 +2122,8 @@ fn gtkFocusLeave(_: *c.GtkEventControllerFocus, ud: ?*anyopaque) callconv(.C) vo
};
}
-/// Adds the unfocused_widget to the overlay. If the unfocused_widget has already been added, this
-/// is a no-op
+/// Adds the unfocused_widget to the overlay. If the unfocused_widget has
+/// already been added, this is a no-op.
pub fn dimSurface(self: *Surface) void {
_ = self.container.window() orelse {
log.warn("dimSurface invalid for container={}", .{self.container});
@@ -2044,17 +2134,23 @@ pub fn dimSurface(self: *Surface) void {
// This means we got unfocused due to it opening.
if (self.context_menu.isVisible()) return;
- if (self.unfocused_widget != null) return;
- self.unfocused_widget = c.gtk_drawing_area_new();
- c.gtk_widget_add_css_class(self.unfocused_widget.?, "unfocused-split");
- c.gtk_overlay_add_overlay(self.overlay, self.unfocused_widget.?);
+ // If there's already an unfocused_widget do nothing;
+ if (self.unfocused_widget) |_| return;
+
+ self.unfocused_widget = unfocused_widget: {
+ const drawing_area = gtk.DrawingArea.new();
+ const unfocused_widget = drawing_area.as(gtk.Widget);
+ unfocused_widget.addCssClass("unfocused-split");
+ self.overlay.addOverlay(unfocused_widget);
+ break :unfocused_widget unfocused_widget;
+ };
}
fn userdataSelf(ud: *anyopaque) *Surface {
return @ptrCast(@alignCast(ud));
}
-fn translateMouseButton(button: c.guint) input.MouseButton {
+fn translateMouseButton(button: c_uint) input.MouseButton {
return switch (button) {
1 => .left,
2 => .middle,
@@ -2077,7 +2173,9 @@ pub fn present(self: *Surface) void {
if (window.notebook.getTabPosition(tab)) |position|
_ = window.notebook.gotoNthTab(position);
}
- c.gtk_window_present(window.window);
+ // FIXME: when Window.zig is converted to zig-gobject
+ const gtk_window: *gtk.Window = @ptrCast(@alignCast(window.window));
+ gtk_window.present();
}
self.grabFocus();
@@ -2103,14 +2201,16 @@ pub fn setSplitZoom(self: *Surface, new_split_zoom: bool) void {
const tab_widget = tab.elem.widget();
const surface_widget = self.primaryWidget();
+ // FIXME: when Tab.zig is converted to zig-gobject
+ const box: *gtk.Box = @ptrCast(@alignCast(tab.box));
if (new_split_zoom) {
self.detachFromSplit();
- c.gtk_box_remove(tab.box, tab_widget);
- c.gtk_box_append(tab.box, surface_widget);
+ box.remove(tab_widget);
+ box.append(surface_widget);
} else {
- c.gtk_box_remove(tab.box, surface_widget);
+ box.remove(surface_widget);
self.attachToSplit();
- c.gtk_box_append(tab.box, tab_widget);
+ box.append(tab_widget);
}
self.zoomed_in = new_split_zoom;
@@ -2123,18 +2223,15 @@ pub fn toggleSplitZoom(self: *Surface) void {
/// Handle items being dropped on our surface.
fn gtkDrop(
- _: *c.GtkDropTarget,
- value: *c.GValue,
- x: f64,
- y: f64,
- ud: ?*anyopaque,
-) callconv(.C) c.gboolean {
- _ = x;
- _ = y;
- const self = userdataSelf(ud.?);
+ _: *gtk.DropTarget,
+ value: *gobject.Value,
+ _: f64,
+ _: f64,
+ self: *Surface,
+) callconv(.C) c_int {
const alloc = self.app.core_app.alloc;
- if (g_value_holds(value, c.G_TYPE_BOXED)) {
+ if (g_value_holds(value, gdk.FileList.getGObjectType())) {
var data = std.ArrayList(u8).init(alloc);
defer data.deinit();
@@ -2143,12 +2240,13 @@ fn gtkDrop(
};
const writer = shell_escape_writer.writer();
- const fl: *c.GdkFileList = @ptrCast(c.g_value_get_boxed(value));
- var l = c.gdk_file_list_get_files(fl);
+ const unboxed = value.getBoxed() orelse return 0;
+ const fl: *gdk.FileList = @ptrCast(@alignCast(unboxed));
+ var list: ?*glib.SList = fl.getFiles();
- while (l != null) : (l = l.*.next) {
- const file: *c.GFile = @ptrCast(l.*.data);
- const path = c.g_file_get_path(file) orelse continue;
+ while (list) |item| : (list = item.f_next) {
+ const file: *gio.File = @ptrCast(@alignCast(item.f_data orelse continue));
+ const path = file.getPath() orelse continue;
writer.writeAll(std.mem.span(path)) catch |err| {
log.err("unable to write path to buffer: {}", .{err});
@@ -2162,7 +2260,38 @@ fn gtkDrop(
const string = data.toOwnedSliceSentinel(0) catch |err| {
log.err("unable to convert to a slice: {}", .{err});
- return 1;
+ return 0;
+ };
+ defer alloc.free(string);
+
+ self.doPaste(string);
+
+ return 1;
+ }
+
+ if (g_value_holds(value, gio.File.getGObjectType())) {
+ const object = value.getObject() orelse return 0;
+ const file = gobject.ext.cast(gio.File, object) orelse return 0;
+ const path = file.getPath() orelse return 0;
+ var data = std.ArrayList(u8).init(alloc);
+ defer data.deinit();
+
+ var shell_escape_writer: internal_os.ShellEscapeWriter(std.ArrayList(u8).Writer) = .{
+ .child_writer = data.writer(),
+ };
+ const writer = shell_escape_writer.writer();
+ writer.writeAll(std.mem.span(path)) catch |err| {
+ log.err("unable to write path to buffer: {}", .{err});
+ return 0;
+ };
+ writer.writeAll("\n") catch |err| {
+ log.err("unable to write to buffer: {}", .{err});
+ return 0;
+ };
+
+ const string = data.toOwnedSliceSentinel(0) catch |err| {
+ log.err("unable to convert to a slice: {}", .{err});
+ return 0;
};
defer alloc.free(string);
@@ -2171,9 +2300,10 @@ fn gtkDrop(
return 1;
}
- if (g_value_holds(value, c.G_TYPE_STRING)) {
- if (c.g_value_get_string(value)) |string| {
- self.doPaste(std.mem.span(string));
+ if (g_value_holds(value, gobject.ext.types.string)) {
+ if (value.getString()) |string| {
+ const text = std.mem.span(string);
+ if (text.len > 0) self.doPaste(text);
}
return 1;
}
@@ -2242,10 +2372,10 @@ pub fn defaultTermioEnv(self: *Surface) !std.process.EnvMap {
/// Check a GValue to see what's type its wrapping. This is equivalent to GTK's
/// `G_VALUE_HOLDS` macro but Zig's C translator does not like it.
-fn g_value_holds(value_: ?*c.GValue, g_type: c.GType) bool {
+fn g_value_holds(value_: ?*gobject.Value, g_type: gobject.Type) bool {
if (value_) |value| {
- if (value.*.g_type == g_type) return true;
- return c.g_type_check_value_holds(value, g_type) != 0;
+ if (value.f_g_type == g_type) return true;
+ return gobject.typeCheckValueHolds(value, g_type) != 0;
}
return false;
}
diff --git a/src/apprt/gtk/Tab.zig b/src/apprt/gtk/Tab.zig
index 78a6253d7..eecbf4358 100644
--- a/src/apprt/gtk/Tab.zig
+++ b/src/apprt/gtk/Tab.zig
@@ -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;
}
diff --git a/src/apprt/gtk/Window.zig b/src/apprt/gtk/Window.zig
index 2e2e0c329..62e3a8b86 100644
--- a/src/apprt/gtk/Window.zig
+++ b/src/apprt/gtk/Window.zig
@@ -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(&gtkRealize), self, null, c.G_CONNECT_DEFAULT);
_ = c.g_signal_connect_data(self.window, "close-request", c.G_CALLBACK(&gtkCloseRequest), self, null, c.G_CONNECT_DEFAULT);
_ = c.g_signal_connect_data(self.window, "destroy", c.G_CALLBACK(&gtkDestroy), self, null, c.G_CONNECT_DEFAULT);
- _ = c.g_signal_connect_data(ec_key_press, "key-pressed", c.G_CALLBACK(&gtkKeyPressed), 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
diff --git a/src/apprt/gtk/key.zig b/src/apprt/gtk/key.zig
index 60f12edca..f59d0080d 100644
--- a/src/apprt/gtk/key.zig
+++ b/src/apprt/gtk/key.zig
@@ -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));
-
- // 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;
-
- defer c.g_free(keys);
- defer c.g_free(keyvals);
+ const layout = event.getLayout();
+
+ // 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 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
diff --git a/src/apprt/gtk/winproto.zig b/src/apprt/gtk/winproto.zig
index 7fc7c0bd3..0f36b1622 100644
--- a/src/apprt/gtk/winproto.zig
+++ b/src/apprt/gtk/winproto.zig
@@ -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),
diff --git a/src/apprt/gtk/winproto/noop.zig b/src/apprt/gtk/winproto/noop.zig
index c71394e7a..33ce65bc4 100644
--- a/src/apprt/gtk/winproto/noop.zig
+++ b/src/apprt/gtk/winproto/noop.zig
@@ -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;
}
diff --git a/src/apprt/gtk/winproto/wayland.zig b/src/apprt/gtk/winproto/wayland.zig
index 389089ec8..1b64d19d9 100644
--- a/src/apprt/gtk/winproto/wayland.zig
+++ b/src/apprt/gtk/winproto/wayland.zig
@@ -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;
}
diff --git a/src/apprt/gtk/winproto/x11.zig b/src/apprt/gtk/winproto/x11.zig
index 72054f0eb..89f8d65e4 100644
--- a/src/apprt/gtk/winproto/x11.zig
+++ b/src/apprt/gtk/winproto/x11.zig
@@ -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;
diff --git a/src/build/SharedDeps.zig b/src/build/SharedDeps.zig
index 4e39c0b39..b9fb93839 100644
--- a/src/build/SharedDeps.zig
+++ b/src/build/SharedDeps.zig
@@ -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;