flow/src/renderer/vaxis/input.zig

456 lines
16 KiB
Zig

const vaxis = @import("vaxis");
const Io = @import("std").Io;
const meta = @import("std").meta;
const unicode = @import("std").unicode;
const FormatOptions = @import("std").fmt.FormatOptions;
pub const key = vaxis.Key;
pub const Key = u21;
pub const Mouse = vaxis.Mouse.Button;
pub const MouseType = @typeInfo(Mouse).@"enum".tag_type;
pub const mouse = struct {
pub const MOTION: Mouse = vaxis.Mouse.Button.none;
pub const BUTTON1: Mouse = vaxis.Mouse.Button.left;
pub const BUTTON2: Mouse = vaxis.Mouse.Button.middle;
pub const BUTTON3: Mouse = vaxis.Mouse.Button.right;
pub const BUTTON4: Mouse = vaxis.Mouse.Button.wheel_up;
pub const BUTTON5: Mouse = vaxis.Mouse.Button.wheel_down;
// pub const BUTTON6: Mouse = vaxis.Mouse.Button.button_6;
// pub const BUTTON7: Mouse = vaxis.Mouse.Button.button_7;
pub const BUTTON8: Mouse = vaxis.Mouse.Button.button_8;
pub const BUTTON9: Mouse = vaxis.Mouse.Button.button_9;
pub const BUTTON10: Mouse = vaxis.Mouse.Button.button_10;
pub const BUTTON11: Mouse = vaxis.Mouse.Button.button_11;
};
/// Does this key represent input?
pub fn is_non_input_key(w: Key) bool {
return switch (w) {
vaxis.Key.insert...vaxis.Key.iso_level_5_shift => true,
vaxis.Key.enter => true,
vaxis.Key.tab => true,
vaxis.Key.escape => true,
vaxis.Key.backspace => true,
else => false,
};
}
pub fn is_modifier(w: Key) bool {
return key.isModifier(.{ .codepoint = w });
}
pub const ModSet = vaxis.Key.Modifiers;
pub const Mods = u8;
pub const mod = struct {
pub const shift: u8 = @bitCast(ModSet{ .shift = true });
pub const alt: u8 = @bitCast(ModSet{ .alt = true });
pub const ctrl: u8 = @bitCast(ModSet{ .ctrl = true });
pub const super: u8 = @bitCast(ModSet{ .super = true });
pub const caps_lock: u8 = @bitCast(ModSet{ .caps_lock = true });
pub const num_lock: u8 = @bitCast(ModSet{ .num_lock = true });
};
pub const Event = u8;
pub const event = struct {
pub const press: Event = 1;
pub const repeat: Event = 2;
pub const release: Event = 3;
};
pub const KeyEvent = struct {
event: Event = 0,
key: Key,
key_unshifted: Key,
modifiers: Mods = 0,
text: []const u8 = "",
pub fn eql(self: @This(), other: @This()) bool {
const self_mods = self.mods_no_shifts();
const other_mods = other.mods_no_shifts();
return self.key == other.key and self_mods == other_mods;
}
pub fn eql_unshifted(self: @This(), other: @This()) bool {
if (self.text.len > 0 or other.text.len > 0) return false;
const self_mods = self.mods_no_caps();
const other_mods = other.mods_no_caps();
return self.key_unshifted == other.key_unshifted and self_mods == other_mods;
}
inline fn mods_no_shifts(self: @This()) Mods {
return if (self.key != self.key_unshifted) self.modifiers & ~(mod.shift | mod.caps_lock) else self.modifiers;
}
inline fn mods_no_caps(self: @This()) Mods {
return self.modifiers & ~mod.caps_lock;
}
pub fn format(self: @This(), writer: anytype) !void {
const mods = self.mods_no_shifts();
return if (self.event > 0)
writer.print("{f}:{f}{f}", .{ event_fmt(self.event), mod_fmt(mods), key_fmt(self.key) })
else
writer.print("{f}{f}", .{ mod_fmt(mods), key_fmt(self.key) });
}
pub fn from_key(keypress: Key) @This() {
return .{
.key = keypress,
.key_unshifted = keypress,
};
}
pub fn from_key_mods(keypress: Key, modifiers: Mods) @This() {
return .{
.key = keypress,
.key_unshifted = keypress,
.modifiers = modifiers,
};
}
pub fn from_key_modset(keypress: Key, modifiers: ModSet) @This() {
return from_key_mods(keypress, @bitCast(modifiers));
}
pub fn from_message(
event_: Event,
keypress_: Key,
keypress_shifted_: Key,
text: []const u8,
modifiers: Mods,
) @This() {
const mods_ = switch (keypress_) {
key.left_super, key.right_super => modifiers & ~mod.super,
key.left_shift, key.right_shift => modifiers & ~mod.shift,
key.left_control, key.right_control => modifiers & ~mod.ctrl,
key.left_alt, key.right_alt => modifiers & ~mod.alt,
else => modifiers,
};
var keypress_shifted: Key = keypress_shifted_;
if (text.len > 0) blk: {
keypress_shifted = tryUtf8Decode(text) catch break :blk;
}
const keypress, const mods = if (keypress_shifted == keypress_)
map_key_to_unshifed_legacy(keypress_shifted, mods_)
else
.{ keypress_, mods_ };
return .{
.event = event_,
.key = keypress_shifted,
.key_unshifted = keypress,
.modifiers = mods,
.text = text,
};
}
};
fn tryUtf8Decode(bytes: []const u8) !u21 {
return switch (bytes.len) {
1 => bytes[0],
2 => blk: {
if (bytes[0] & 0b11100000 != 0b11000000) break :blk error.Utf8Encoding2Invalid;
break :blk unicode.utf8Decode2(bytes[0..2].*);
},
3 => blk: {
if (bytes[0] & 0b11110000 != 0b11100000) break :blk error.Utf8Encoding3Invalid;
break :blk unicode.utf8Decode3(bytes[0..3].*);
},
4 => blk: {
if (bytes[0] & 0b11111000 != 0b11110000) break :blk error.Utf8Encoding4Invalid;
break :blk unicode.utf8Decode4(bytes[0..4].*);
},
else => error.Utf8CodepointLengthInvalid,
};
}
pub fn ucs32_to_utf8(ucs32: []const u32, utf8: []u8) !usize {
return @intCast(try unicode.utf8Encode(@intCast(ucs32[0]), utf8));
}
pub const utils = struct {
pub fn key_id_string(k: Key) []const u8 {
return switch (k) {
vaxis.Key.enter => "enter",
vaxis.Key.tab => "tab",
vaxis.Key.escape => "escape",
vaxis.Key.space => "space",
vaxis.Key.backspace => "backspace",
vaxis.Key.insert => "insert",
vaxis.Key.delete => "delete",
vaxis.Key.left => "left",
vaxis.Key.right => "right",
vaxis.Key.up => "up",
vaxis.Key.down => "down",
vaxis.Key.page_down => "page_down",
vaxis.Key.page_up => "page_up",
vaxis.Key.home => "home",
vaxis.Key.end => "end",
vaxis.Key.caps_lock => "caps_lock",
vaxis.Key.scroll_lock => "scroll_lock",
vaxis.Key.num_lock => "num_lock",
vaxis.Key.print_screen => "print_screen",
vaxis.Key.pause => "pause",
vaxis.Key.menu => "menu",
vaxis.Key.f1 => "f1",
vaxis.Key.f2 => "f2",
vaxis.Key.f3 => "f3",
vaxis.Key.f4 => "f4",
vaxis.Key.f5 => "f5",
vaxis.Key.f6 => "f6",
vaxis.Key.f7 => "f7",
vaxis.Key.f8 => "f8",
vaxis.Key.f9 => "f9",
vaxis.Key.f10 => "f10",
vaxis.Key.f11 => "f11",
vaxis.Key.f12 => "f12",
vaxis.Key.f13 => "f13",
vaxis.Key.f14 => "f14",
vaxis.Key.f15 => "f15",
vaxis.Key.f16 => "f16",
vaxis.Key.f17 => "f17",
vaxis.Key.f18 => "f18",
vaxis.Key.f19 => "f19",
vaxis.Key.f20 => "f20",
vaxis.Key.f21 => "f21",
vaxis.Key.f22 => "f22",
vaxis.Key.f23 => "f23",
vaxis.Key.f24 => "f24",
vaxis.Key.f25 => "f25",
vaxis.Key.f26 => "f26",
vaxis.Key.f27 => "f27",
vaxis.Key.f28 => "f28",
vaxis.Key.f29 => "f29",
vaxis.Key.f30 => "f30",
vaxis.Key.f31 => "f31",
vaxis.Key.f32 => "f32",
vaxis.Key.f33 => "f33",
vaxis.Key.f34 => "f34",
vaxis.Key.f35 => "f35",
vaxis.Key.kp_decimal => "kp_decimal",
vaxis.Key.kp_divide => "kp_divide",
vaxis.Key.kp_multiply => "kp_multiply",
vaxis.Key.kp_subtract => "kp_subtract",
vaxis.Key.kp_add => "kp_add",
vaxis.Key.kp_enter => "kp_enter",
vaxis.Key.kp_equal => "kp_equal",
vaxis.Key.kp_separator => "kp_separator",
vaxis.Key.kp_left => "kp_left",
vaxis.Key.kp_right => "kp_right",
vaxis.Key.kp_up => "kp_up",
vaxis.Key.kp_down => "kp_down",
vaxis.Key.kp_page_up => "kp_page_up",
vaxis.Key.kp_page_down => "kp_page_down",
vaxis.Key.kp_home => "kp_home",
vaxis.Key.kp_end => "kp_end",
vaxis.Key.kp_insert => "kp_insert",
vaxis.Key.kp_delete => "kp_delete",
vaxis.Key.kp_begin => "kp_begin",
vaxis.Key.media_play => "media_play",
vaxis.Key.media_pause => "media_pause",
vaxis.Key.media_play_pause => "media_play_pause",
vaxis.Key.media_reverse => "media_reverse",
vaxis.Key.media_stop => "media_stop",
vaxis.Key.media_fast_forward => "media_fast_forward",
vaxis.Key.media_rewind => "media_rewind",
vaxis.Key.media_track_next => "media_track_next",
vaxis.Key.media_track_previous => "media_track_previous",
vaxis.Key.media_record => "media_record",
vaxis.Key.lower_volume => "lower_volume",
vaxis.Key.raise_volume => "raise_volume",
vaxis.Key.mute_volume => "mute_volume",
vaxis.Key.left_shift => "left_shift",
vaxis.Key.left_control => "left_control",
vaxis.Key.left_alt => "left_alt",
vaxis.Key.left_super => "left_super",
vaxis.Key.left_hyper => "left_hyper",
vaxis.Key.left_meta => "left_meta",
vaxis.Key.right_shift => "right_shift",
vaxis.Key.right_control => "right_control",
vaxis.Key.right_alt => "right_alt",
vaxis.Key.right_super => "right_super",
vaxis.Key.right_hyper => "right_hyper",
vaxis.Key.right_meta => "right_meta",
vaxis.Key.iso_level_3_shift => "iso_level_3_shift",
vaxis.Key.iso_level_5_shift => "iso_level_5_shift",
else => "",
};
}
pub fn key_id_string_short(k: Key) []const u8 {
return switch (k) {
vaxis.Key.enter => "ret",
vaxis.Key.tab => "tab",
vaxis.Key.escape => "esc",
vaxis.Key.space => "sp",
vaxis.Key.backspace => "bs",
vaxis.Key.insert => "ins",
vaxis.Key.delete => "del",
vaxis.Key.left => "",
vaxis.Key.right => "",
vaxis.Key.up => "",
vaxis.Key.down => "",
vaxis.Key.page_down => "pgdn",
vaxis.Key.page_up => "pgup",
vaxis.Key.left_shift => "lshft",
vaxis.Key.left_control => "lctrl",
vaxis.Key.left_alt => "lalt",
vaxis.Key.left_super => "lsuper",
vaxis.Key.left_hyper => "lhyper",
vaxis.Key.left_meta => "lmeta",
vaxis.Key.right_shift => "rshft",
vaxis.Key.right_control => "rctrl",
vaxis.Key.right_alt => "ralt",
vaxis.Key.right_super => "rsuper",
vaxis.Key.right_hyper => "rhyper",
vaxis.Key.right_meta => "rmeta",
vaxis.Key.iso_level_3_shift => "iso3",
vaxis.Key.iso_level_5_shift => "iso5",
else => key_id_string(k),
};
}
pub fn button_id_string(m: Mouse) []const u8 {
return switch (m) {
mouse.MOTION => "motion",
mouse.BUTTON1 => "button1",
mouse.BUTTON2 => "button2",
mouse.BUTTON3 => "button3",
mouse.BUTTON4 => "button4",
mouse.BUTTON5 => "button5",
// mouse.BUTTON6 => "button6",
// mouse.BUTTON7 => "button7",
mouse.BUTTON8 => "button8",
mouse.BUTTON9 => "button9",
mouse.BUTTON10 => "button10",
mouse.BUTTON11 => "button11",
else => "",
};
}
};
pub fn key_event_short_fmt(ke: KeyEvent) struct {
ke: KeyEvent,
pub fn format(self: @This(), writer: anytype) Io.Writer.Error!void {
return writer.print("{f}{f}", .{ mod_short_fmt(self.ke.modifiers), key_short_fmt(self.ke.key) });
}
} {
return .{ .ke = ke };
}
pub fn event_fmt(evt: Event) struct {
event: Event,
pub fn format(self: @This(), writer: anytype) Io.Writer.Error!void {
return switch (self.event) {
event.press => writer.writeAll("press"),
event.repeat => writer.writeAll("repeat"),
event.release => writer.writeAll("release"),
else => {},
};
}
} {
return .{ .event = evt };
}
pub fn event_short_fmt(evt: Event) struct {
event: Event,
pub fn format(self: @This(), writer: anytype) Io.Writer.Error!void {
return switch (self.event) {
event.press => writer.writeAll("P"),
event.repeat => writer.writeAll("RP"),
event.release => writer.writeAll("R"),
else => {},
};
}
} {
return .{ .event = evt };
}
pub fn key_fmt(key_: Key) struct {
key: Key,
pub fn format(self: @This(), writer: anytype) Io.Writer.Error!void {
var key_string = utils.key_id_string(self.key);
var buf: [6]u8 = undefined;
if (key_string.len == 0) {
const bytes = ucs32_to_utf8(&[_]u32{self.key}, &buf) catch return error.WriteFailed;
key_string = buf[0..bytes];
}
try writer.writeAll(key_string);
}
} {
return .{ .key = key_ };
}
pub fn key_short_fmt(key_: Key) struct {
key: Key,
pub fn format(self: @This(), writer: anytype) Io.Writer.Error!void {
var key_string = utils.key_id_string_short(self.key);
var buf: [6]u8 = undefined;
if (key_string.len == 0) {
const bytes = try ucs32_to_utf8(&[_]u32{self.key}, &buf);
key_string = buf[0..bytes];
}
try writer.writeAll(key_string);
}
} {
return .{ .key = key_ };
}
pub fn mod_fmt(mods: Mods) struct {
modifiers: Mods,
pub fn format(self: @This(), writer: anytype) Io.Writer.Error!void {
const modset: ModSet = @bitCast(self.modifiers);
if (modset.super) try writer.writeAll("super+");
if (modset.ctrl) try writer.writeAll("ctrl+");
if (modset.alt) try writer.writeAll("alt+");
if (modset.shift) try writer.writeAll("shift+");
}
} {
return .{ .modifiers = mods };
}
pub fn mod_short_fmt(mods: Mods) struct {
modifiers: Mods,
pub fn format(self: @This(), writer: anytype) Io.Writer.Error!void {
const modset: ModSet = @bitCast(self.modifiers);
if (modset.super) try writer.writeAll("Super-");
if (modset.ctrl) try writer.writeAll("C-");
if (modset.alt) try writer.writeAll("A-");
if (modset.shift) try writer.writeAll("S-");
}
} {
return .{ .modifiers = mods };
}
fn map_key_to_unshifed_legacy(keypress_shifted: Key, mods: Mods) struct { Key, Mods } {
return switch (keypress_shifted) {
'A'...'Z' => .{ keypress_shifted + ('a' - 'A'), mods | mod.shift },
'!' => .{ '1', mods | mod.shift },
'@' => .{ '2', mods | mod.shift },
'#' => .{ '3', mods | mod.shift },
'$' => .{ '4', mods | mod.shift },
'%' => .{ '5', mods | mod.shift },
'^' => .{ '6', mods | mod.shift },
'&' => .{ '7', mods | mod.shift },
'*' => .{ '8', mods | mod.shift },
'(' => .{ '9', mods | mod.shift },
')' => .{ '0', mods | mod.shift },
'_' => .{ '-', mods | mod.shift },
'+' => .{ '=', mods | mod.shift },
'~' => .{ '`', mods | mod.shift },
'{' => .{ '[', mods | mod.shift },
'}' => .{ ']', mods | mod.shift },
'|' => .{ '\\', mods | mod.shift },
':' => .{ ';', mods | mod.shift },
'"' => .{ '\'', mods | mod.shift },
'<' => .{ ',', mods | mod.shift },
'>' => .{ '.', mods | mod.shift },
'?' => .{ '/', mods | mod.shift },
else => .{ keypress_shifted, mods },
};
}