gtk: port inspector & key handling to zig-gobject

This commit is contained in:
Leah Amelia Chen
2025-03-18 10:39:54 +01:00
parent e3fbbe8fe3
commit 1ee9c85954
4 changed files with 354 additions and 283 deletions

View File

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

View File

@@ -3,10 +3,12 @@ const ImguiWidget = @This();
const std = @import("std");
const assert = std.debug.assert;
const gdk = @import("gdk");
const gtk = @import("gtk");
const cimgui = @import("cimgui");
const c = @import("c.zig").c;
const key = @import("key.zig");
const gl = @import("opengl");
const key = @import("key.zig");
const input = @import("../../input.zig");
const log = std.log.scoped(.gtk_imgui_widget);
@@ -16,8 +18,8 @@ render_callback: ?*const fn (?*anyopaque) void = null,
render_userdata: ?*anyopaque = null,
/// Our OpenGL widget
gl_area: *c.GtkGLArea,
im_context: *c.GtkIMContext,
gl_area: *gtk.GLArea,
im_context: *gtk.IMContext,
/// ImGui Context
ig_ctx: *cimgui.c.ImGuiContext,
@@ -36,65 +38,145 @@ pub fn init(self: *ImguiWidget) !void {
io.BackendPlatformName = "ghostty_gtk";
// Our OpenGL area for drawing
const gl_area = c.gtk_gl_area_new();
c.gtk_gl_area_set_auto_render(@ptrCast(gl_area), 1);
const gl_area = gtk.GLArea.new();
gl_area.setAutoRender(@intFromBool(true));
// The GL area has to be focusable so that it can receive events
c.gtk_widget_set_focusable(@ptrCast(gl_area), 1);
c.gtk_widget_set_focus_on_click(@ptrCast(gl_area), 1);
gl_area.as(gtk.Widget).setFocusable(@intFromBool(true));
gl_area.as(gtk.Widget).setFocusOnClick(@intFromBool(true));
// 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(gl_area), @ptrCast(gesture_click));
const gesture_click = gtk.GestureClick.new();
errdefer gesture_click.unref();
gesture_click.as(gtk.GestureSingle).setButton(0);
gl_area.as(gtk.Widget).addController(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(gl_area), ec_motion);
const ec_motion = gtk.EventControllerMotion.new();
errdefer ec_motion.unref();
gl_area.as(gtk.Widget).addController(ec_motion.as(gtk.EventController));
// Scroll events
const ec_scroll = c.gtk_event_controller_scroll_new(
c.GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES |
c.GTK_EVENT_CONTROLLER_SCROLL_DISCRETE,
);
errdefer c.g_object_unref(ec_scroll);
c.gtk_widget_add_controller(@ptrCast(gl_area), ec_scroll);
const ec_scroll = gtk.EventControllerScroll.new(.flags_both_axes);
errdefer ec_scroll.unref();
gl_area.as(gtk.Widget).addController(ec_scroll.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(gl_area), ec_focus);
const ec_focus = gtk.EventControllerFocus.new();
errdefer ec_focus.unref();
gl_area.as(gtk.Widget).addController(ec_focus.as(gtk.EventController));
// 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(gl_area), ec_key);
errdefer c.gtk_widget_remove_controller(@ptrCast(gl_area), ec_key);
const ec_key = gtk.EventControllerKey.new();
errdefer ec_key.unref();
gl_area.as(gtk.Widget).addController(ec_key.as(gtk.EventController));
errdefer gl_area.as(gtk.Widget).removeController(ec_key.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();
// Signals
_ = 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, "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, "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_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(ec_key, "key-pressed", c.G_CALLBACK(&gtkKeyPressed), self, null, c.G_CONNECT_DEFAULT);
_ = c.g_signal_connect_data(ec_key, "key-released", c.G_CALLBACK(&gtkKeyReleased), 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_scroll, "scroll", c.G_CALLBACK(&gtkMouseScroll), 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(im_context, "commit", c.G_CALLBACK(&gtkInputCommit), self, null, c.G_CONNECT_DEFAULT);
_ = gtk.Widget.signals.realize.connect(
gl_area,
*ImguiWidget,
gtkRealize,
self,
.{},
);
_ = gtk.Widget.signals.unrealize.connect(
gl_area,
*ImguiWidget,
gtkUnrealize,
self,
.{},
);
_ = 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.* = .{
.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
/// so that the UI can be refreshed.
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.
@@ -130,7 +212,7 @@ fn newFrame(self: *ImguiWidget) !void {
self.instant = now;
}
fn translateMouseButton(button: c.guint) ?c_int {
fn translateMouseButton(button: c_uint) ?c_int {
return switch (button) {
1 => cimgui.c.ImGuiMouseButton_Left,
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 {
_ = v;
fn gtkDestroy(_: *gtk.GLArea, self: *ImguiWidget) callconv(.C) void {
log.debug("imgui widget destroy", .{});
const self: *ImguiWidget = @ptrCast(@alignCast(ud.?));
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", .{});
// 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});
area.makeCurrent();
if (area.getError()) |err| {
log.err("surface failed to realize: {s}", .{err.f_message orelse "(unknown)"});
return;
}
// realize means that our OpenGL context is ready, so we can now
// initialize the ImgUI OpenGL backend for our context.
const self: *ImguiWidget = @ptrCast(@alignCast(ud.?));
cimgui.c.igSetCurrentContext(self.ig_ctx);
_ = 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;
log.debug("gl surface unrealized", .{});
const self: *ImguiWidget = @ptrCast(@alignCast(ud.?));
cimgui.c.igSetCurrentContext(self.ig_ctx);
cimgui.ImGui_ImplOpenGL3_Shutdown();
}
fn gtkResize(area: *c.GtkGLArea, width: c.gint, height: c.gint, ud: ?*anyopaque) callconv(.C) void {
const self: *ImguiWidget = @ptrCast(@alignCast(ud.?));
fn gtkResize(area: *gtk.GLArea, width: c_int, height: c_int, self: *ImguiWidget) callconv(.C) void {
cimgui.c.igSetCurrentContext(self.ig_ctx);
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={}", .{
width,
height,
@@ -197,10 +273,7 @@ fn gtkResize(area: *c.GtkGLArea, width: c.gint, height: c.gint, ud: ?*anyopaque)
active_style.* = style.*;
}
fn gtkRender(area: *c.GtkGLArea, ctx: *c.GdkGLContext, ud: ?*anyopaque) callconv(.C) c.gboolean {
_ = area;
_ = ctx;
const self: *ImguiWidget = @ptrCast(@alignCast(ud.?));
fn gtkRender(_: *gtk.GLArea, _: *gdk.GLContext, self: *ImguiWidget) callconv(.C) c_int {
cimgui.c.igSetCurrentContext(self.ig_ctx);
// 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(
_: *c.GtkEventControllerMotion,
x: c.gdouble,
y: c.gdouble,
ud: ?*anyopaque,
_: *gtk.EventControllerMotion,
x: f64,
y: f64,
self: *ImguiWidget,
) callconv(.C) void {
const self: *ImguiWidget = @ptrCast(@alignCast(ud.?));
cimgui.c.igSetCurrentContext(self.ig_ctx);
const io: *cimgui.c.ImGuiIO = cimgui.c.igGetIO();
const scale_factor: f64 = @floatFromInt(c.gtk_widget_get_scale_factor(
@ptrCast(self.gl_area),
));
const scale_factor: f64 = @floatFromInt(self.gl_area.as(gtk.Widget).getScaleFactor());
cimgui.c.ImGuiIO_AddMousePosEvent(
io,
@floatCast(x * scale_factor),
@@ -250,48 +320,46 @@ fn gtkMouseMotion(
}
fn gtkMouseDown(
gesture: *c.GtkGestureClick,
_: c.gint,
_: c.gdouble,
_: c.gdouble,
ud: ?*anyopaque,
gesture: *gtk.GestureClick,
_: c_int,
_: f64,
_: f64,
self: *ImguiWidget,
) callconv(.C) void {
const self: *ImguiWidget = @ptrCast(@alignCast(ud.?));
self.queueRender();
cimgui.c.igSetCurrentContext(self.ig_ctx);
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| {
cimgui.c.ImGuiIO_AddMouseButtonEvent(io, button, true);
}
}
fn gtkMouseUp(
gesture: *c.GtkGestureClick,
_: c.gint,
_: c.gdouble,
_: c.gdouble,
ud: ?*anyopaque,
gesture: *gtk.GestureClick,
_: c_int,
_: f64,
_: f64,
self: *ImguiWidget,
) callconv(.C) void {
const self: *ImguiWidget = @ptrCast(@alignCast(ud.?));
self.queueRender();
cimgui.c.igSetCurrentContext(self.ig_ctx);
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| {
cimgui.c.ImGuiIO_AddMouseButtonEvent(io, button, false);
}
}
fn gtkMouseScroll(
_: *c.GtkEventControllerScroll,
x: c.gdouble,
y: c.gdouble,
ud: ?*anyopaque,
) callconv(.C) void {
const self: *ImguiWidget = @ptrCast(@alignCast(ud.?));
_: *gtk.EventControllerScroll,
x: f64,
y: f64,
self: *ImguiWidget,
) callconv(.C) c_int {
self.queueRender();
cimgui.c.igSetCurrentContext(self.ig_ctx);
@@ -301,10 +369,11 @@ fn gtkMouseScroll(
@floatCast(x),
@floatCast(-y),
);
return @intFromBool(true);
}
fn gtkFocusEnter(_: *c.GtkEventControllerFocus, ud: ?*anyopaque) callconv(.C) void {
const self: *ImguiWidget = @ptrCast(@alignCast(ud.?));
fn gtkFocusEnter(_: *gtk.EventControllerFocus, self: *ImguiWidget) callconv(.C) void {
self.queueRender();
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);
}
fn gtkFocusLeave(_: *c.GtkEventControllerFocus, ud: ?*anyopaque) callconv(.C) void {
const self: *ImguiWidget = @ptrCast(@alignCast(ud.?));
fn gtkFocusLeave(_: *gtk.EventControllerFocus, self: *ImguiWidget) callconv(.C) void {
self.queueRender();
cimgui.c.igSetCurrentContext(self.ig_ctx);
@@ -322,11 +390,10 @@ fn gtkFocusLeave(_: *c.GtkEventControllerFocus, ud: ?*anyopaque) callconv(.C) vo
}
fn gtkInputCommit(
_: *c.GtkIMContext,
_: *gtk.IMMulticontext,
bytes: [*:0]u8,
ud: ?*anyopaque,
self: *ImguiWidget,
) callconv(.C) void {
const self: *ImguiWidget = @ptrCast(@alignCast(ud.?));
self.queueRender();
cimgui.c.igSetCurrentContext(self.ig_ctx);
@@ -335,44 +402,53 @@ fn gtkInputCommit(
}
fn gtkKeyPressed(
ec_key: *c.GtkEventControllerKey,
keyval: c.guint,
keycode: c.guint,
gtk_mods: c.GdkModifierType,
ud: ?*anyopaque,
) callconv(.C) c.gboolean {
return if (keyEvent(.press, ec_key, keyval, keycode, gtk_mods, ud)) 1 else 0;
ec_key: *gtk.EventControllerKey,
keyval: c_uint,
keycode: c_uint,
gtk_mods: gdk.ModifierType,
self: *ImguiWidget,
) callconv(.C) c_int {
return @intFromBool(self.keyEvent(
.press,
ec_key,
keyval,
keycode,
gtk_mods,
));
}
fn gtkKeyReleased(
ec_key: *c.GtkEventControllerKey,
keyval: c.guint,
keycode: c.guint,
state: c.GdkModifierType,
ud: ?*anyopaque,
) callconv(.C) c.gboolean {
return if (keyEvent(.release, ec_key, keyval, keycode, state, ud)) 1 else 0;
ec_key: *gtk.EventControllerKey,
keyval: c_uint,
keycode: c_uint,
gtk_mods: gdk.ModifierType,
self: *ImguiWidget,
) callconv(.C) void {
_ = self.keyEvent(
.release,
ec_key,
keyval,
keycode,
gtk_mods,
);
}
fn keyEvent(
self: *ImguiWidget,
action: input.Action,
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,
) bool {
_ = keycode;
const self: *ImguiWidget = @ptrCast(@alignCast(ud.?));
self.queueRender();
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(@bitCast(gtk_mods));
const mods = key.translateMods(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);
@@ -386,9 +462,9 @@ fn keyEvent(
}
// Try to process the event as text
const event = c.gtk_event_controller_get_current_event(@ptrCast(ec_key));
if (event != null)
_ = c.gtk_im_context_filter_keypress(self.im_context, event);
if (ec_key.as(gtk.EventController).getCurrentEvent()) |event| {
_ = self.im_context.filterKeypress(event);
}
return true;
}

View File

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

View File

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