apprt: key/charCallback, input supports all glfw keys

This commit is contained in:
Mitchell Hashimoto
2022-12-30 14:45:03 -08:00
parent ff1f1d8925
commit 946383eb77
3 changed files with 312 additions and 146 deletions

View File

@ -394,8 +394,6 @@ pub fn create(alloc: Allocator, app: *App, config: *const Config) !*Window {
// }, .{ .width = null, .height = null });
// Setup our callbacks and user data
winsys.window.setCharCallback(charCallback);
winsys.window.setKeyCallback(keyCallback);
winsys.window.setFocusCallback(focusCallback);
winsys.window.setRefreshCallback(refreshCallback);
winsys.window.setScrollCallback(scrollCallback);
@ -720,51 +718,46 @@ pub fn sizeCallback(self: *Window, size: apprt.WindowSize) !void {
try self.io_thread.wakeup.send();
}
fn charCallback(window: glfw.Window, codepoint: u21) void {
pub fn charCallback(self: *Window, codepoint: u21) !void {
const tracy = trace(@src());
defer tracy.end();
const win = window.getUserPointer(Window) orelse return;
// Dev Mode
if (DevMode.enabled and DevMode.instance.visible) {
// If the event was handled by imgui, ignore it.
if (imgui.IO.get()) |io| {
if (io.cval().WantCaptureKeyboard) {
win.queueRender() catch |err|
log.err("error scheduling render timer err={}", .{err});
try self.queueRender();
}
} else |_| {}
}
// Ignore if requested. See field docs for more information.
if (win.ignore_char) {
win.ignore_char = false;
if (self.ignore_char) {
self.ignore_char = false;
return;
}
// Critical area
{
win.renderer_state.mutex.lock();
defer win.renderer_state.mutex.unlock();
self.renderer_state.mutex.lock();
defer self.renderer_state.mutex.unlock();
// Clear the selction if we have one.
if (win.io.terminal.selection != null) {
win.io.terminal.selection = null;
win.queueRender() catch |err|
log.err("error scheduling render in charCallback err={}", .{err});
if (self.io.terminal.selection != null) {
self.io.terminal.selection = null;
try self.queueRender();
}
// We want to scroll to the bottom
// TODO: detect if we're at the bottom to avoid the render call here.
win.io.terminal.scrollViewport(.{ .bottom = {} }) catch |err|
log.err("error scrolling viewport err={}", .{err});
try self.io.terminal.scrollViewport(.{ .bottom = {} });
}
// Ask our IO thread to write the data
var data: termio.Message.WriteReq.Small.Array = undefined;
data[0] = @intCast(u8, codepoint);
_ = win.io_thread.mailbox.push(.{
_ = self.io_thread.mailbox.push(.{
.write_small = .{
.data = data,
.len = 1,
@ -772,38 +765,31 @@ fn charCallback(window: glfw.Window, codepoint: u21) void {
}, .{ .forever = {} });
// After sending all our messages we have to notify our IO thread
win.io_thread.wakeup.send() catch {};
try self.io_thread.wakeup.send();
}
fn keyCallback(
window: glfw.Window,
key: glfw.Key,
scancode: i32,
action: glfw.Action,
mods: glfw.Mods,
) void {
pub fn keyCallback(
self: *Window,
action: input.Action,
key: input.Key,
mods: input.Mods,
) !void {
const tracy = trace(@src());
defer tracy.end();
const win = window.getUserPointer(Window) orelse return;
// Dev Mode
if (DevMode.enabled and DevMode.instance.visible) {
// If the event was handled by imgui, ignore it.
if (imgui.IO.get()) |io| {
if (io.cval().WantCaptureKeyboard) {
win.queueRender() catch |err|
log.err("error scheduling render timer err={}", .{err});
try self.queueRender();
}
} else |_| {}
}
// Reset the ignore char setting. If we didn't handle the char
// by here, we aren't going to get it so we just reset this.
win.ignore_char = false;
//log.info("KEY {} {} {} {}", .{ key, scancode, mods, action });
_ = scancode;
self.ignore_char = false;
if (action == .press or action == .repeat) {
// Convert our glfw input into a platform agnostic trigger. When we
@ -811,74 +797,12 @@ fn keyCallback(
// into a function. For now, this is the only place we do it so we just
// put it right here.
const trigger: input.Binding.Trigger = .{
.mods = @bitCast(input.Mods, mods),
.key = switch (key) {
.a => .a,
.b => .b,
.c => .c,
.d => .d,
.e => .e,
.f => .f,
.g => .g,
.h => .h,
.i => .i,
.j => .j,
.k => .k,
.l => .l,
.m => .m,
.n => .n,
.o => .o,
.p => .p,
.q => .q,
.r => .r,
.s => .s,
.t => .t,
.u => .u,
.v => .v,
.w => .w,
.x => .x,
.y => .y,
.z => .z,
.zero => .zero,
.one => .one,
.two => .three,
.three => .four,
.four => .four,
.five => .five,
.six => .six,
.seven => .seven,
.eight => .eight,
.nine => .nine,
.up => .up,
.down => .down,
.right => .right,
.left => .left,
.home => .home,
.end => .end,
.page_up => .page_up,
.page_down => .page_down,
.escape => .escape,
.F1 => .f1,
.F2 => .f2,
.F3 => .f3,
.F4 => .f4,
.F5 => .f5,
.F6 => .f6,
.F7 => .f7,
.F8 => .f8,
.F9 => .f9,
.F10 => .f10,
.F11 => .f11,
.F12 => .f12,
.grave_accent => .grave_accent,
.minus => .minus,
.equal => .equal,
else => .invalid,
},
.mods = mods,
.key = key,
};
//log.warn("BINDING TRIGGER={}", .{trigger});
if (win.config.keybind.set.get(trigger)) |binding_action| {
if (self.config.keybind.set.get(trigger)) |binding_action| {
//log.warn("BINDING ACTION={}", .{binding_action});
switch (binding_action) {
@ -886,13 +810,13 @@ fn keyCallback(
.ignore => {},
.csi => |data| {
_ = win.io_thread.mailbox.push(.{
_ = self.io_thread.mailbox.push(.{
.write_stable = "\x1B[",
}, .{ .forever = {} });
_ = win.io_thread.mailbox.push(.{
_ = self.io_thread.mailbox.push(.{
.write_stable = data,
}, .{ .forever = {} });
win.io_thread.wakeup.send() catch {};
try self.io_thread.wakeup.send();
},
.cursor_key => |ck| {
@ -900,37 +824,37 @@ fn keyCallback(
// in cursor keys mode. We're in "normal" mode if cursor
// keys mdoe is NOT set.
const normal = normal: {
win.renderer_state.mutex.lock();
defer win.renderer_state.mutex.unlock();
break :normal !win.io.terminal.modes.cursor_keys;
self.renderer_state.mutex.lock();
defer self.renderer_state.mutex.unlock();
break :normal !self.io.terminal.modes.cursor_keys;
};
if (normal) {
_ = win.io_thread.mailbox.push(.{
_ = self.io_thread.mailbox.push(.{
.write_stable = ck.normal,
}, .{ .forever = {} });
} else {
_ = win.io_thread.mailbox.push(.{
_ = self.io_thread.mailbox.push(.{
.write_stable = ck.application,
}, .{ .forever = {} });
}
win.io_thread.wakeup.send() catch {};
try self.io_thread.wakeup.send();
},
.copy_to_clipboard => {
// We can read from the renderer state without holding
// the lock because only we will write to this field.
if (win.io.terminal.selection) |sel| {
var buf = win.io.terminal.screen.selectionString(
win.alloc,
if (self.io.terminal.selection) |sel| {
var buf = self.io.terminal.screen.selectionString(
self.alloc,
sel,
win.config.@"clipboard-trim-trailing-spaces",
self.config.@"clipboard-trim-trailing-spaces",
) catch |err| {
log.err("error reading selection string err={}", .{err});
return;
};
defer win.alloc.free(buf);
defer self.alloc.free(buf);
glfw.setClipboardString(buf) catch |err| {
log.err("error setting clipboard string err={}", .{err});
@ -947,99 +871,99 @@ fn keyCallback(
if (data.len > 0) {
const bracketed = bracketed: {
win.renderer_state.mutex.lock();
defer win.renderer_state.mutex.unlock();
break :bracketed win.io.terminal.modes.bracketed_paste;
self.renderer_state.mutex.lock();
defer self.renderer_state.mutex.unlock();
break :bracketed self.io.terminal.modes.bracketed_paste;
};
if (bracketed) {
_ = win.io_thread.mailbox.push(.{
_ = self.io_thread.mailbox.push(.{
.write_stable = "\x1B[200~",
}, .{ .forever = {} });
}
_ = win.io_thread.mailbox.push(termio.Message.writeReq(
win.alloc,
_ = self.io_thread.mailbox.push(try termio.Message.writeReq(
self.alloc,
data,
) catch unreachable, .{ .forever = {} });
), .{ .forever = {} });
if (bracketed) {
_ = win.io_thread.mailbox.push(.{
_ = self.io_thread.mailbox.push(.{
.write_stable = "\x1B[201~",
}, .{ .forever = {} });
}
win.io_thread.wakeup.send() catch {};
try self.io_thread.wakeup.send();
}
},
.increase_font_size => |delta| {
log.debug("increase font size={}", .{delta});
var size = win.font_size;
var size = self.font_size;
size.points +|= delta;
win.setFontSize(size);
self.setFontSize(size);
},
.decrease_font_size => |delta| {
log.debug("decrease font size={}", .{delta});
var size = win.font_size;
var size = self.font_size;
size.points = @max(1, size.points -| delta);
win.setFontSize(size);
self.setFontSize(size);
},
.reset_font_size => {
log.debug("reset font size", .{});
var size = win.font_size;
size.points = win.config.@"font-size";
win.setFontSize(size);
var size = self.font_size;
size.points = self.config.@"font-size";
self.setFontSize(size);
},
.toggle_dev_mode => if (DevMode.enabled) {
DevMode.instance.visible = !DevMode.instance.visible;
win.queueRender() catch unreachable;
try self.queueRender();
} else log.warn("dev mode was not compiled into this binary", .{}),
.new_window => {
_ = win.app.mailbox.push(.{
_ = self.app.mailbox.push(.{
.new_window = .{
.font_size = if (win.config.@"window-inherit-font-size")
win.font_size
.font_size = if (self.config.@"window-inherit-font-size")
self.font_size
else
null,
},
}, .{ .instant = {} });
win.app.wakeup();
self.app.wakeup();
},
.new_tab => {
_ = win.app.mailbox.push(.{
_ = self.app.mailbox.push(.{
.new_tab = .{
.parent = win,
.parent = self,
.font_size = if (win.config.@"window-inherit-font-size")
win.font_size
.font_size = if (self.config.@"window-inherit-font-size")
self.font_size
else
null,
},
}, .{ .instant = {} });
win.app.wakeup();
self.app.wakeup();
},
.close_window => win.window.setShouldClose(true),
.close_window => self.windowing_system.setShouldClose(),
.quit => {
_ = win.app.mailbox.push(.{
_ = self.app.mailbox.push(.{
.quit = {},
}, .{ .instant = {} });
win.app.wakeup();
self.app.wakeup();
},
}
// Bindings always result in us ignoring the char if printable
win.ignore_char = true;
self.ignore_char = true;
// No matter what, if there is a binding then we are done.
return;
@ -1099,7 +1023,7 @@ fn keyCallback(
// Ask our IO thread to write the data
var data: termio.Message.WriteReq.Small.Array = undefined;
data[0] = @intCast(u8, char);
_ = win.io_thread.mailbox.push(.{
_ = self.io_thread.mailbox.push(.{
.write_small = .{
.data = data,
.len = 1,
@ -1107,7 +1031,7 @@ fn keyCallback(
}, .{ .forever = {} });
// After sending all our messages we have to notify our IO thread
win.io_thread.wakeup.send() catch {};
try self.io_thread.wakeup.send();
}
}
}

View File

@ -7,9 +7,11 @@ const std = @import("std");
const builtin = @import("builtin");
const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
const trace = @import("tracy").trace;
const glfw = @import("glfw");
const objc = @import("objc");
const App = @import("../App.zig");
const input = @import("../input.zig");
const internal_os = @import("../os/main.zig");
const renderer = @import("../renderer.zig");
const Renderer = renderer.Renderer;
@ -85,6 +87,8 @@ pub const Window = struct {
// Set our callbacks
win.setUserPointer(core_win);
win.setSizeCallback(sizeCallback);
win.setCharCallback(charCallback);
win.setKeyCallback(keyCallback);
// Build our result
return Window{
@ -158,6 +162,12 @@ pub const Window = struct {
return apprt.WindowSize{ .width = size.width, .height = size.height };
}
/// Set the flag that notes this window should be closed for the next
/// iteration of the event loop.
pub fn setShouldClose(self: *Window) void {
self.window.setShouldClose(true);
}
fn sizeCallback(window: glfw.Window, width: i32, height: i32) void {
_ = width;
_ = height;
@ -177,4 +187,167 @@ pub const Window = struct {
return;
};
}
fn charCallback(window: glfw.Window, codepoint: u21) void {
const tracy = trace(@src());
defer tracy.end();
const core_win = window.getUserPointer(CoreWindow) orelse return;
core_win.charCallback(codepoint) catch |err| {
log.err("error in char callback err={}", .{err});
return;
};
}
fn keyCallback(
window: glfw.Window,
glfw_key: glfw.Key,
scancode: i32,
glfw_action: glfw.Action,
glfw_mods: glfw.Mods,
) void {
_ = scancode;
const tracy = trace(@src());
defer tracy.end();
// Convert our glfw types into our input types
const mods = @bitCast(input.Mods, glfw_mods);
const action: input.Action = switch (glfw_action) {
.release => .release,
.press => .press,
.repeat => .repeat,
};
const key: input.Key = switch (glfw_key) {
.a => .a,
.b => .b,
.c => .c,
.d => .d,
.e => .e,
.f => .f,
.g => .g,
.h => .h,
.i => .i,
.j => .j,
.k => .k,
.l => .l,
.m => .m,
.n => .n,
.o => .o,
.p => .p,
.q => .q,
.r => .r,
.s => .s,
.t => .t,
.u => .u,
.v => .v,
.w => .w,
.x => .x,
.y => .y,
.z => .z,
.zero => .zero,
.one => .one,
.two => .three,
.three => .four,
.four => .four,
.five => .five,
.six => .six,
.seven => .seven,
.eight => .eight,
.nine => .nine,
.up => .up,
.down => .down,
.right => .right,
.left => .left,
.home => .home,
.end => .end,
.page_up => .page_up,
.page_down => .page_down,
.escape => .escape,
.F1 => .f1,
.F2 => .f2,
.F3 => .f3,
.F4 => .f4,
.F5 => .f5,
.F6 => .f6,
.F7 => .f7,
.F8 => .f8,
.F9 => .f9,
.F10 => .f10,
.F11 => .f11,
.F12 => .f12,
.F13 => .f13,
.F14 => .f14,
.F15 => .f15,
.F16 => .f16,
.F17 => .f17,
.F18 => .f18,
.F19 => .f19,
.F20 => .f20,
.F21 => .f21,
.F22 => .f22,
.F23 => .f23,
.F24 => .f24,
.F25 => .f25,
.kp_0 => .kp_0,
.kp_1 => .kp_1,
.kp_2 => .kp_2,
.kp_3 => .kp_3,
.kp_4 => .kp_4,
.kp_5 => .kp_5,
.kp_6 => .kp_6,
.kp_7 => .kp_7,
.kp_8 => .kp_8,
.kp_9 => .kp_9,
.kp_decimal => .kp_decimal,
.kp_divide => .kp_divide,
.kp_multiply => .kp_multiply,
.kp_subtract => .kp_subtract,
.kp_add => .kp_add,
.kp_enter => .kp_enter,
.kp_equal => .kp_equal,
.grave_accent => .grave_accent,
.minus => .minus,
.equal => .equal,
.space => .space,
.semicolon => .semicolon,
.apostrophe => .apostrophe,
.comma => .comma,
.period => .period,
.slash => .slash,
.left_bracket => .left_bracket,
.right_bracket => .right_bracket,
.backslash => .backslash,
.enter => .enter,
.tab => .tab,
.backspace => .backspace,
.insert => .insert,
.delete => .delete,
.caps_lock => .caps_lock,
.scroll_lock => .scroll_lock,
.num_lock => .num_lock,
.print_screen => .print_screen,
.pause => .pause,
.left_shift => .left_shift,
.left_control => .left_control,
.left_alt => .left_alt,
.left_super => .left_super,
.right_shift => .right_shift,
.right_control => .right_control,
.right_alt => .right_alt,
.right_super => .right_super,
.menu,
.world_1,
.world_2,
.unknown,
=> .invalid,
};
const core_win = window.getUserPointer(CoreWindow) orelse return;
core_win.keyCallback(action, key, mods) catch |err| {
log.err("error in key callback err={}", .{err});
return;
};
}
};

View File

@ -13,6 +13,13 @@ pub const Mods = packed struct {
_padding: u2 = 0,
};
/// The action associated with an input event.
pub const Action = enum {
release,
press,
repeat,
};
/// The set of keys that can map to keybindings. These have no fixed enum
/// values because we map platform-specific keys to this set. Note that
/// this only needs to accomodate what maps to a key. If a key is not bound
@ -61,10 +68,19 @@ pub const Key = enum {
eight,
nine,
// other
// puncuation
semicolon,
space,
apostrophe,
comma,
grave_accent, // `
period,
slash,
minus,
equal,
left_bracket, // [
right_bracket, // ]
backslash, // /
// control
up,
@ -73,10 +89,21 @@ pub const Key = enum {
left,
home,
end,
insert,
delete,
caps_lock,
scroll_lock,
num_lock,
page_up,
page_down,
escape,
enter,
tab,
backspace,
print_screen,
pause,
// function keys
f1,
f2,
f3,
@ -89,6 +116,48 @@ pub const Key = enum {
f10,
f11,
f12,
f13,
f14,
f15,
f16,
f17,
f18,
f19,
f20,
f21,
f22,
f23,
f24,
f25,
// keypad
kp_0,
kp_1,
kp_2,
kp_3,
kp_4,
kp_5,
kp_6,
kp_7,
kp_8,
kp_9,
kp_decimal,
kp_divide,
kp_multiply,
kp_subtract,
kp_add,
kp_enter,
kp_equal,
// modifiers
left_shift,
left_control,
left_alt,
left_super,
right_shift,
right_control,
right_alt,
right_super,
// To support more keys (there are obviously more!) add them here
// and ensure the mapping is up to date in the Window key handler.