632 lines
23 KiB
Zig
632 lines
23 KiB
Zig
const std = @import("std");
|
|
const mach = @import("../main.zig");
|
|
const Core = @import("../Core.zig");
|
|
const gpu = mach.gpu;
|
|
const Event = Core.Event;
|
|
const KeyEvent = Core.KeyEvent;
|
|
const MouseButtonEvent = Core.MouseButtonEvent;
|
|
const MouseButton = Core.MouseButton;
|
|
const Size = Core.Size;
|
|
const DisplayMode = Core.DisplayMode;
|
|
const CursorShape = Core.CursorShape;
|
|
const VSyncMode = Core.VSyncMode;
|
|
const CursorMode = Core.CursorMode;
|
|
const Position = Core.Position;
|
|
const Key = Core.Key;
|
|
const KeyMods = Core.KeyMods;
|
|
const objc = @import("objc");
|
|
|
|
const log = std.log.scoped(.mach);
|
|
|
|
pub const Darwin = @This();
|
|
|
|
pub const Native = struct {
|
|
window: *objc.app_kit.Window = undefined,
|
|
view: *objc.mach.View = undefined,
|
|
};
|
|
|
|
pub const Context = struct {
|
|
core: *Core,
|
|
window_id: mach.ObjectID,
|
|
};
|
|
|
|
pub fn run(comptime on_each_update_fn: anytype, args_tuple: std.meta.ArgsTuple(@TypeOf(on_each_update_fn))) noreturn {
|
|
const Args = @TypeOf(args_tuple);
|
|
const args_bytes = std.mem.asBytes(&args_tuple);
|
|
const ArgsBytes = @TypeOf(args_bytes.*);
|
|
const Helper = struct {
|
|
// TODO: port libdispatch and use it instead of doing this directly.
|
|
extern "System" fn dispatch_async(queue: *anyopaque, block: *objc.foundation.Block(fn () void)) void;
|
|
extern "System" var _dispatch_main_q: anyopaque;
|
|
pub fn cCallback(block: *objc.foundation.BlockLiteral(ArgsBytes)) callconv(.C) void {
|
|
const args: *Args = @ptrCast(&block.context);
|
|
if (@call(.auto, on_each_update_fn, args.*) catch false) {
|
|
dispatch_async(&_dispatch_main_q, block.asBlockWithSignature(fn () void));
|
|
} else {
|
|
// We copied the block when we called `setRunBlock()`, so we release it here when the looping will end.
|
|
block.release();
|
|
}
|
|
}
|
|
};
|
|
var block_literal = objc.foundation.stackBlockLiteral(Helper.cCallback, args_bytes.*, null, null);
|
|
|
|
// `NSApplicationMain()` and `UIApplicationMain()` never return, so there's no point in trying to add any kind of cleanup work here.
|
|
const ns_app = objc.app_kit.Application.sharedApplication();
|
|
|
|
const delegate = objc.mach.AppDelegate.allocInit();
|
|
delegate.setRunBlock(block_literal.asBlock().copy());
|
|
ns_app.setDelegate(@ptrCast(delegate));
|
|
|
|
ns_app.run();
|
|
|
|
unreachable;
|
|
// TODO: support UIKit.
|
|
}
|
|
|
|
pub fn tick(core: *Core) !void {
|
|
var windows = core.windows.slice();
|
|
while (windows.next()) |window_id| {
|
|
const core_window = windows.get(window_id);
|
|
|
|
if (core_window.native) |native| {
|
|
const native_window: *objc.app_kit.Window = native.window;
|
|
const native_view: *objc.mach.View = native.view;
|
|
|
|
if (core.windows.updated(window_id, .color)) {
|
|
switch (core_window.color) {
|
|
.transparent => |wc| {
|
|
const color = objc.app_kit.Color.colorWithRed_green_blue_alpha(
|
|
wc.color.r,
|
|
wc.color.g,
|
|
wc.color.b,
|
|
wc.color.a,
|
|
);
|
|
native_window.setBackgroundColor(color);
|
|
native_window.setTitlebarAppearsTransparent(true);
|
|
native_view.layer().setOpaque(false);
|
|
},
|
|
.solid => |wc| {
|
|
const color = objc.app_kit.Color.colorWithRed_green_blue_alpha(
|
|
wc.color.r,
|
|
wc.color.g,
|
|
wc.color.b,
|
|
wc.color.a,
|
|
);
|
|
native_window.setBackgroundColor(color);
|
|
native_window.setTitlebarAppearsTransparent(false);
|
|
native_view.layer().setOpaque(true);
|
|
},
|
|
.system => {
|
|
native_window.setTitlebarAppearsTransparent(false);
|
|
native_view.layer().setOpaque(true);
|
|
},
|
|
}
|
|
}
|
|
|
|
if (core.windows.updated(window_id, .title)) {
|
|
const string = objc.foundation.String.allocInit();
|
|
defer string.release();
|
|
native.window.setTitle(string.initWithUTF8String(core_window.title));
|
|
}
|
|
|
|
if (core.windows.updated(window_id, .width) or core.windows.updated(window_id, .height)) {
|
|
var frame = native_window.frame();
|
|
frame.size.width = @floatFromInt(core.windows.get(window_id, .width));
|
|
frame.size.height = @floatFromInt(core.windows.get(window_id, .height));
|
|
native_window.setFrame_display_animate(native_window.frameRectForContentRect(frame), true, true);
|
|
}
|
|
} else {
|
|
try initWindow(core, window_id);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn initWindow(
|
|
core: *Core,
|
|
window_id: mach.ObjectID,
|
|
) !void {
|
|
var core_window = core.windows.getValue(window_id);
|
|
// If the application is not headless, we need to make the application a genuine UI application
|
|
// by setting the activation policy, this moves the process to foreground
|
|
// TODO: Only call this on the first window creation
|
|
_ = objc.app_kit.Application.sharedApplication().setActivationPolicy(objc.app_kit.ApplicationActivationPolicyRegular);
|
|
|
|
const metal_descriptor = try core.allocator.create(gpu.Surface.DescriptorFromMetalLayer);
|
|
const layer = objc.quartz_core.MetalLayer.new();
|
|
defer layer.release();
|
|
|
|
if (core_window.color == .transparent) layer.setOpaque(false);
|
|
|
|
metal_descriptor.* = .{
|
|
.layer = layer,
|
|
};
|
|
core_window.surface_descriptor = .{};
|
|
core_window.surface_descriptor.next_in_chain = .{ .from_metal_layer = metal_descriptor };
|
|
|
|
const screen = objc.app_kit.Screen.mainScreen();
|
|
const rect = objc.core_graphics.Rect{
|
|
.origin = .{ .x = 0, .y = 0 },
|
|
.size = .{ .width = @floatFromInt(core_window.width), .height = @floatFromInt(core_window.height) },
|
|
};
|
|
|
|
const window_style =
|
|
(if (core_window.display_mode == .fullscreen) objc.app_kit.WindowStyleMaskFullScreen else 0) |
|
|
(if (core_window.display_mode == .windowed) objc.app_kit.WindowStyleMaskTitled else 0) |
|
|
(if (core_window.display_mode == .windowed) objc.app_kit.WindowStyleMaskClosable else 0) |
|
|
(if (core_window.display_mode == .windowed) objc.app_kit.WindowStyleMaskMiniaturizable else 0) |
|
|
(if (core_window.display_mode == .windowed) objc.app_kit.WindowStyleMaskResizable else 0);
|
|
// (if (core_window.display_mode == .windowed) objc.app_kit.WindowStyleMaskFullSizeContentView else 0);
|
|
|
|
const native_window_opt: ?*objc.app_kit.Window = objc.app_kit.Window.alloc().initWithContentRect_styleMask_backing_defer_screen(
|
|
rect,
|
|
window_style,
|
|
objc.app_kit.BackingStoreBuffered,
|
|
false,
|
|
screen,
|
|
);
|
|
if (native_window_opt) |native_window| {
|
|
const framebuffer_scale: f32 = @floatCast(native_window.backingScaleFactor());
|
|
const window_width: f32 = @floatFromInt(core_window.width);
|
|
const window_height: f32 = @floatFromInt(core_window.height);
|
|
|
|
core_window.framebuffer_width = @intFromFloat(window_width * framebuffer_scale);
|
|
core_window.framebuffer_height = @intFromFloat(window_height * framebuffer_scale);
|
|
|
|
native_window.setReleasedWhenClosed(false);
|
|
|
|
var view = objc.mach.View.allocInit();
|
|
|
|
// initWithFrame is overridden in our MACHView, which creates a tracking area for mouse tracking
|
|
view = view.initWithFrame(rect);
|
|
view.setLayer(@ptrCast(layer));
|
|
|
|
const context = try core.allocator.create(Context);
|
|
context.* = .{ .core = core, .window_id = window_id };
|
|
// TODO(core): free this allocation
|
|
|
|
{
|
|
var keyDown = objc.foundation.stackBlockLiteral(
|
|
ViewCallbacks.keyDown,
|
|
context,
|
|
null,
|
|
null,
|
|
);
|
|
view.setBlock_keyDown(keyDown.asBlock().copy());
|
|
|
|
var keyUp = objc.foundation.stackBlockLiteral(
|
|
ViewCallbacks.keyUp,
|
|
context,
|
|
null,
|
|
null,
|
|
);
|
|
view.setBlock_keyUp(keyUp.asBlock().copy());
|
|
|
|
var flagsChanged = objc.foundation.stackBlockLiteral(
|
|
ViewCallbacks.flagsChanged,
|
|
context,
|
|
null,
|
|
null,
|
|
);
|
|
view.setBlock_flagsChanged(flagsChanged.asBlock().copy());
|
|
|
|
var magnify = objc.foundation.stackBlockLiteral(
|
|
ViewCallbacks.magnify,
|
|
context,
|
|
null,
|
|
null,
|
|
);
|
|
view.setBlock_magnify(magnify.asBlock().copy());
|
|
|
|
var mouseMoved = objc.foundation.stackBlockLiteral(
|
|
ViewCallbacks.mouseMoved,
|
|
context,
|
|
null,
|
|
null,
|
|
);
|
|
view.setBlock_mouseMoved(mouseMoved.asBlock().copy());
|
|
|
|
var mouseDown = objc.foundation.stackBlockLiteral(
|
|
ViewCallbacks.mouseDown,
|
|
context,
|
|
null,
|
|
null,
|
|
);
|
|
view.setBlock_mouseDown(mouseDown.asBlock().copy());
|
|
|
|
var mouseUp = objc.foundation.stackBlockLiteral(
|
|
ViewCallbacks.mouseUp,
|
|
context,
|
|
null,
|
|
null,
|
|
);
|
|
view.setBlock_mouseUp(mouseUp.asBlock().copy());
|
|
|
|
var scrollWheel = objc.foundation.stackBlockLiteral(
|
|
ViewCallbacks.scrollWheel,
|
|
context,
|
|
null,
|
|
null,
|
|
);
|
|
view.setBlock_scrollWheel(scrollWheel.asBlock().copy());
|
|
}
|
|
native_window.setContentView(@ptrCast(view));
|
|
native_window.center();
|
|
native_window.setIsVisible(true);
|
|
native_window.makeKeyAndOrderFront(null);
|
|
|
|
switch (core_window.color) {
|
|
.transparent => |wc| {
|
|
const color = objc.app_kit.Color.colorWithRed_green_blue_alpha(
|
|
wc.color.r,
|
|
wc.color.g,
|
|
wc.color.b,
|
|
wc.color.a,
|
|
);
|
|
native_window.setBackgroundColor(color);
|
|
native_window.setTitlebarAppearsTransparent(true);
|
|
},
|
|
.solid => |wc| {
|
|
const color = objc.app_kit.Color.colorWithRed_green_blue_alpha(
|
|
wc.color.r,
|
|
wc.color.g,
|
|
wc.color.b,
|
|
wc.color.a,
|
|
);
|
|
native_window.setBackgroundColor(color);
|
|
},
|
|
.system => {},
|
|
}
|
|
|
|
const string = objc.foundation.String.allocInit();
|
|
defer string.release();
|
|
native_window.setTitle(string.initWithUTF8String(core_window.title));
|
|
|
|
const delegate = objc.mach.WindowDelegate.allocInit();
|
|
defer native_window.setDelegate(@ptrCast(delegate));
|
|
{
|
|
var windowDidResize = objc.foundation.stackBlockLiteral(
|
|
WindowDelegateCallbacks.windowDidResize,
|
|
context,
|
|
null,
|
|
null,
|
|
);
|
|
delegate.setBlock_windowDidResize(windowDidResize.asBlock().copy());
|
|
|
|
var windowShouldClose = objc.foundation.stackBlockLiteral(
|
|
WindowDelegateCallbacks.windowShouldClose,
|
|
context,
|
|
null,
|
|
null,
|
|
);
|
|
delegate.setBlock_windowShouldClose(windowShouldClose.asBlock().copy());
|
|
}
|
|
|
|
// Set core_window.native, which we use to check if a window is initialized
|
|
// Then call core.initWindow to finish initializing the window
|
|
core_window.native = .{ .window = native_window, .view = view };
|
|
core.windows.setValueRaw(window_id, core_window);
|
|
try core.initWindow(window_id);
|
|
} else std.debug.panic("mach: window failed to initialize", .{});
|
|
}
|
|
|
|
const WindowDelegateCallbacks = struct {
|
|
pub fn windowDidResize(block: *objc.foundation.BlockLiteral(*Context)) callconv(.C) void {
|
|
const core: *Core = block.context.core;
|
|
|
|
var core_window = core.windows.getValue(block.context.window_id);
|
|
|
|
if (core_window.native) |native| {
|
|
const native_window: *objc.app_kit.Window = native.window;
|
|
|
|
const frame = native_window.frame();
|
|
const content_rect = native_window.contentRectForFrameRect(frame);
|
|
|
|
core_window.width = @intFromFloat(content_rect.size.width);
|
|
core_window.height = @intFromFloat(content_rect.size.height);
|
|
|
|
const framebuffer_scale: f32 = @floatCast(native_window.backingScaleFactor());
|
|
const window_width: f32 = @floatFromInt(core_window.width);
|
|
const window_height: f32 = @floatFromInt(core_window.height);
|
|
|
|
core_window.framebuffer_width = @intFromFloat(window_width * framebuffer_scale);
|
|
core_window.framebuffer_height = @intFromFloat(window_height * framebuffer_scale);
|
|
|
|
core_window.swap_chain_descriptor.width = core_window.framebuffer_width;
|
|
core_window.swap_chain_descriptor.height = core_window.framebuffer_height;
|
|
core_window.swap_chain.release();
|
|
|
|
core_window.swap_chain = core_window.device.createSwapChain(core_window.surface, &core_window.swap_chain_descriptor);
|
|
}
|
|
|
|
core.windows.setValueRaw(block.context.window_id, core_window);
|
|
|
|
core.pushEvent(.{ .window_resize = .{
|
|
.window_id = block.context.window_id,
|
|
.size = .{ .width = core_window.width, .height = core_window.height },
|
|
} });
|
|
}
|
|
|
|
pub fn windowShouldClose(block: *objc.foundation.BlockLiteral(*Context)) callconv(.C) bool {
|
|
const core: *Core = block.context.core;
|
|
core.pushEvent(.{ .close = .{ .window_id = block.context.window_id } });
|
|
|
|
// TODO: This should just attempt to close the window, not the entire program, unless
|
|
// this is the only window.
|
|
return false;
|
|
}
|
|
};
|
|
|
|
const ViewCallbacks = struct {
|
|
pub fn mouseMoved(block: *objc.foundation.BlockLiteral(*Context), event: *objc.app_kit.Event) callconv(.C) void {
|
|
const core: *Core = block.context.core;
|
|
const window_id = block.context.window_id;
|
|
|
|
const mouse_location = event.locationInWindow();
|
|
|
|
const window_height: f32 = @floatFromInt(core.windows.get(window_id, .height));
|
|
|
|
core.pushEvent(.{ .mouse_motion = .{
|
|
.window_id = window_id,
|
|
.pos = .{ .x = mouse_location.x, .y = window_height - mouse_location.y },
|
|
} });
|
|
}
|
|
|
|
pub fn mouseDown(block: *objc.foundation.BlockLiteral(*Context), event: *objc.app_kit.Event) callconv(.C) void {
|
|
const core: *Core = block.context.core;
|
|
const window_id = block.context.window_id;
|
|
|
|
core.pushEvent(.{ .mouse_press = .{
|
|
.window_id = window_id,
|
|
.button = @enumFromInt(event.buttonNumber()),
|
|
.pos = .{ .x = event.locationInWindow().x, .y = event.locationInWindow().y },
|
|
.mods = machModifierFromModifierFlag(event.modifierFlags()),
|
|
} });
|
|
}
|
|
pub fn mouseUp(block: *objc.foundation.BlockLiteral(*Context), event: *objc.app_kit.Event) callconv(.C) void {
|
|
const core: *Core = block.context.core;
|
|
const window_id = block.context.window_id;
|
|
|
|
core.pushEvent(.{ .mouse_release = .{
|
|
.window_id = window_id,
|
|
.button = @enumFromInt(event.buttonNumber()),
|
|
.pos = .{ .x = event.locationInWindow().x, .y = event.locationInWindow().y },
|
|
.mods = machModifierFromModifierFlag(event.modifierFlags()),
|
|
} });
|
|
}
|
|
|
|
pub fn scrollWheel(block: *objc.foundation.BlockLiteral(*Context), event: *objc.app_kit.Event) callconv(.C) void {
|
|
const core: *Core = block.context.core;
|
|
const window_id = block.context.window_id;
|
|
|
|
var scroll_delta_x = event.scrollingDeltaX();
|
|
var scroll_delta_y = event.scrollingDeltaY();
|
|
|
|
if (event.hasPreciseScrollingDeltas()) {
|
|
scroll_delta_x *= 0.1;
|
|
scroll_delta_y *= 0.1;
|
|
}
|
|
|
|
core.pushEvent(.{ .mouse_scroll = .{
|
|
.window_id = window_id,
|
|
.xoffset = @floatCast(scroll_delta_x),
|
|
.yoffset = @floatCast(scroll_delta_y),
|
|
} });
|
|
}
|
|
|
|
// This is currently only supported on macOS using a trackpad
|
|
pub fn magnify(block: *objc.foundation.BlockLiteral(*Context), event: *objc.app_kit.Event) callconv(.C) void {
|
|
const core: *Core = block.context.core;
|
|
const window_id = block.context.window_id;
|
|
|
|
core.pushEvent(.{ .magnify = .{
|
|
.window_id = window_id,
|
|
.magnification = @floatCast(event.magnification()),
|
|
} });
|
|
}
|
|
|
|
pub fn keyDown(block: *objc.foundation.BlockLiteral(*Context), event: *objc.app_kit.Event) callconv(.C) void {
|
|
const core: *Core = block.context.core;
|
|
const window_id = block.context.window_id;
|
|
if (event.isARepeat()) {
|
|
core.pushEvent(.{ .key_repeat = .{
|
|
.window_id = window_id,
|
|
.key = machKeyFromKeycode(event.keyCode()),
|
|
.mods = machModifierFromModifierFlag(event.modifierFlags()),
|
|
} });
|
|
} else {
|
|
core.pushEvent(.{ .key_press = .{
|
|
.window_id = window_id,
|
|
.key = machKeyFromKeycode(event.keyCode()),
|
|
.mods = machModifierFromModifierFlag(event.modifierFlags()),
|
|
} });
|
|
}
|
|
}
|
|
|
|
pub fn keyUp(block: *objc.foundation.BlockLiteral(*Context), event: *objc.app_kit.Event) callconv(.C) void {
|
|
const core: *Core = block.context.core;
|
|
const window_id = block.context.window_id;
|
|
|
|
core.pushEvent(.{ .key_release = .{
|
|
.window_id = window_id,
|
|
.key = machKeyFromKeycode(event.keyCode()),
|
|
.mods = machModifierFromModifierFlag(event.modifierFlags()),
|
|
} });
|
|
}
|
|
|
|
pub fn flagsChanged(block: *objc.foundation.BlockLiteral(*Context), event: *objc.app_kit.Event) callconv(.C) void {
|
|
const core: *Core = block.context.core;
|
|
const window_id = block.context.window_id;
|
|
|
|
const key = machKeyFromKeycode(event.keyCode());
|
|
const mods = machModifierFromModifierFlag(event.modifierFlags());
|
|
|
|
const key_flag = switch (key) {
|
|
.left_shift, .right_shift => objc.app_kit.EventModifierFlagShift,
|
|
.left_control, .right_control => objc.app_kit.EventModifierFlagControl,
|
|
.left_alt, .right_alt => objc.app_kit.EventModifierFlagOption,
|
|
.left_super, .right_super => objc.app_kit.EventModifierFlagCommand,
|
|
.caps_lock => objc.app_kit.EventModifierFlagCapsLock,
|
|
else => 0,
|
|
};
|
|
|
|
if (event.modifierFlags() & key_flag != 0) {
|
|
if (core.input_state.isKeyPressed(key)) {
|
|
core.pushEvent(.{ .key_release = .{ .window_id = window_id, .key = key, .mods = mods } });
|
|
} else {
|
|
core.pushEvent(.{ .key_press = .{ .window_id = window_id, .key = key, .mods = mods } });
|
|
}
|
|
} else {
|
|
core.pushEvent(.{ .key_release = .{ .window_id = window_id, .key = key, .mods = mods } });
|
|
}
|
|
}
|
|
};
|
|
|
|
fn machModifierFromModifierFlag(modifier_flag: usize) Core.KeyMods {
|
|
var modifier: Core.KeyMods = .{
|
|
.alt = false,
|
|
.caps_lock = false,
|
|
.control = false,
|
|
.num_lock = false,
|
|
.shift = false,
|
|
.super = false,
|
|
};
|
|
|
|
if (modifier_flag & objc.app_kit.EventModifierFlagOption != 0)
|
|
modifier.alt = true;
|
|
|
|
if (modifier_flag & objc.app_kit.EventModifierFlagCapsLock != 0)
|
|
modifier.caps_lock = true;
|
|
|
|
if (modifier_flag & objc.app_kit.EventModifierFlagControl != 0)
|
|
modifier.control = true;
|
|
|
|
if (modifier_flag & objc.app_kit.EventModifierFlagShift != 0)
|
|
modifier.shift = true;
|
|
|
|
if (modifier_flag & objc.app_kit.EventModifierFlagCommand != 0)
|
|
modifier.super = true;
|
|
|
|
return modifier;
|
|
}
|
|
|
|
fn machKeyFromKeycode(keycode: c_ushort) Core.Key {
|
|
comptime var table: [256]Key = undefined;
|
|
comptime for (&table, 1..) |*ptr, i| {
|
|
ptr.* = switch (i) {
|
|
0x35 => .escape,
|
|
0x12 => .one,
|
|
0x13 => .two,
|
|
0x14 => .three,
|
|
0x15 => .four,
|
|
0x17 => .five,
|
|
0x16 => .six,
|
|
0x1A => .seven,
|
|
0x1C => .eight,
|
|
0x19 => .nine,
|
|
0x1D => .zero,
|
|
0x1B => .minus,
|
|
0x18 => .equal,
|
|
0x33 => .backspace,
|
|
0x30 => .tab,
|
|
0x0C => .q,
|
|
0x0D => .w,
|
|
0x0E => .e,
|
|
0x0F => .r,
|
|
0x11 => .t,
|
|
0x10 => .y,
|
|
0x20 => .u,
|
|
0x22 => .i,
|
|
0x1F => .o,
|
|
0x23 => .p,
|
|
0x21 => .left_bracket,
|
|
0x1E => .right_bracket,
|
|
0x24 => .enter,
|
|
0x3B => .left_control,
|
|
0x00 => .a,
|
|
0x01 => .s,
|
|
0x02 => .d,
|
|
0x03 => .f,
|
|
0x05 => .g,
|
|
0x04 => .h,
|
|
0x26 => .j,
|
|
0x28 => .k,
|
|
0x25 => .l,
|
|
0x29 => .semicolon,
|
|
0x27 => .apostrophe,
|
|
0x32 => .grave,
|
|
0x38 => .left_shift,
|
|
//0x2A => .backslash, // Iso backslash instead?
|
|
0x06 => .z,
|
|
0x07 => .x,
|
|
0x08 => .c,
|
|
0x09 => .v,
|
|
0x0B => .b,
|
|
0x2D => .n,
|
|
0x2E => .m,
|
|
0x2B => .comma,
|
|
0x2F => .period,
|
|
0x2C => .slash,
|
|
0x3C => .right_shift,
|
|
0x43 => .kp_multiply,
|
|
0x3A => .left_alt,
|
|
0x31 => .space,
|
|
0x39 => .caps_lock,
|
|
0x7A => .f1,
|
|
0x78 => .f2,
|
|
0x63 => .f3,
|
|
0x76 => .f4,
|
|
0x60 => .f5,
|
|
0x61 => .f6,
|
|
0x62 => .f7,
|
|
0x64 => .f8,
|
|
0x65 => .f9,
|
|
0x6D => .f10,
|
|
0x59 => .kp_7,
|
|
0x5B => .kp_8,
|
|
0x5C => .kp_9,
|
|
0x4E => .kp_subtract,
|
|
0x56 => .kp_4,
|
|
0x57 => .kp_5,
|
|
0x58 => .kp_6,
|
|
0x45 => .kp_add,
|
|
0x53 => .kp_1,
|
|
0x54 => .kp_2,
|
|
0x55 => .kp_3,
|
|
0x52 => .kp_0,
|
|
0x41 => .kp_decimal,
|
|
0x69 => .print,
|
|
0x2A => .iso_backslash,
|
|
0x67 => .f11,
|
|
0x6F => .f12,
|
|
0x51 => .kp_equal,
|
|
//0x64 => .f13, GLFW doesnt have a f13?
|
|
0x6B => .f14,
|
|
0x71 => .f15,
|
|
0x6A => .f16,
|
|
0x40 => .f17,
|
|
0x4F => .f18,
|
|
0x50 => .f19,
|
|
0x5A => .f20,
|
|
0x4C => .kp_enter,
|
|
0x3E => .right_control,
|
|
0x4B => .kp_divide,
|
|
0x3D => .right_alt,
|
|
0x47 => .num_lock,
|
|
0x73 => .home,
|
|
0x7E => .up,
|
|
0x74 => .page_up,
|
|
0x7B => .left,
|
|
0x7C => .right,
|
|
0x77 => .end,
|
|
0x7D => .down,
|
|
0x79 => .page_down,
|
|
0x72 => .insert,
|
|
0x75 => .delete,
|
|
0x37 => .left_super,
|
|
0x36 => .right_super,
|
|
0x6E => .menu,
|
|
else => .unknown,
|
|
};
|
|
};
|
|
return if (keycode > 0 and keycode <= table.len) table[keycode - 1] else if (keycode == 0) .a else .unknown;
|
|
}
|