gtk: disable kinetic scrolling for trackpads until 4.20.1

Until gtk 4.20.1 trackpads have kinetic scrolling behavior regardless
of `Gtk.ScrolledWindow.kinetic_scrolling`. As a workaround, set
EventControllerScroll.kinetic to false on all controllers.

`observeControllers()` has this warning:
> Calling this function will enable extra internal bookkeeping to track controllers and emit signals on the returned listmodel. It may slow down operations a lot.
> Applications should try hard to avoid calling this function because of the slowdowns.

but judging from the [source](5301a91f1c/gtk/gtkwidget.c (L12375-L12383))
this is a one time penalty since we free the result immediately afterwards.

Fixes https://github.com/ghostty-org/ghostty/discussions/11460
This commit is contained in:
Michael Stevens
2026-03-13 12:19:33 -04:00
parent 846599b97e
commit c0a124f3ca
2 changed files with 21 additions and 1 deletions

View File

@@ -2,6 +2,7 @@ const std = @import("std");
const adw = @import("adw");
const gobject = @import("gobject");
const gtk = @import("gtk");
const gtk_version = @import("../gtk_version.zig");
const gresource = @import("../build/gresource.zig");
const Common = @import("../class.zig").Common;
@@ -59,11 +60,29 @@ pub const SurfaceScrolledWindow = extern struct {
config: ?*Config = null,
config_binding: ?*gobject.Binding = null,
surface: ?*Surface = null,
scrolled_window: *gtk.ScrolledWindow,
pub var offset: c_int = 0;
};
fn init(self: *Self, _: *Class) callconv(.c) void {
gtk.Widget.initTemplate(self.as(gtk.Widget));
if (gtk_version.runtimeUntil(4, 20, 1)) self.disableKineticScroll();
}
fn disableKineticScroll(self: *Self) void {
// Until gtk 4.20.1 trackpads have kinetic scrolling behavior regardless
// of `Gtk.ScrolledWindow.kinetic_scrolling`. As a workaround, disable
// EventControllerScroll.kinetic
const controllers = self.private().scrolled_window.as(gtk.Widget).observeControllers();
defer controllers.unref();
var i: c_uint = 0;
while (controllers.getObject(i)) |obj| : (i += 1) {
defer obj.unref();
const controller = gobject.ext.cast(gtk.EventControllerScroll, obj) orelse continue;
var flags = controller.getFlags();
flags.kinetic = false;
controller.setFlags(flags);
}
}
fn dispose(self: *Self) callconv(.c) void {
@@ -189,6 +208,7 @@ pub const SurfaceScrolledWindow = extern struct {
// Bindings
class.bindTemplateCallback("scrollbar_policy", &closureScrollbarPolicy);
class.bindTemplateCallback("notify_surface", &propSurface);
class.bindTemplateChildPrivate("scrolled_window", .{});
// Properties
gobject.ext.registerProperties(class, &.{

View File

@@ -4,7 +4,7 @@ using Adw 1;
template $GhostttySurfaceScrolledWindow: Adw.Bin {
notify::surface => $notify_surface();
Gtk.ScrolledWindow {
Gtk.ScrolledWindow scrolled_window {
hscrollbar-policy: never;
vscrollbar-policy: bind $scrollbar_policy(template.config) as <Gtk.PolicyType>;
}