core: re-implement wayland
This commit is contained in:
parent
f7141c01e2
commit
9ffaddd77a
1 changed files with 861 additions and 0 deletions
861
src/core/linux/Wayland.zig
Normal file
861
src/core/linux/Wayland.zig
Normal file
|
|
@ -0,0 +1,861 @@
|
|||
const std = @import("std");
|
||||
const mach = @import("../../main.zig");
|
||||
const gpu = mach.gpu;
|
||||
const Linux = @import("../Linux.zig");
|
||||
const Core = @import("../../Core.zig");
|
||||
const InitOptions = Core.InitOptions;
|
||||
const log = std.log.scoped(.mach);
|
||||
|
||||
pub const Wayland = @This();
|
||||
|
||||
pub const c = @cImport({
|
||||
@cInclude("wayland-client-protocol.h");
|
||||
@cInclude("wayland-xdg-shell-client-protocol.h");
|
||||
@cInclude("wayland-xdg-decoration-client-protocol.h");
|
||||
@cInclude("wayland-viewporter-client-protocol.h");
|
||||
@cInclude("wayland-relative-pointer-unstable-v1-client-protocol.h");
|
||||
@cInclude("wayland-pointer-constraints-unstable-v1-client-protocol.h");
|
||||
@cInclude("wayland-idle-inhibit-unstable-v1-client-protocol.h");
|
||||
@cInclude("xkbcommon/xkbcommon.h");
|
||||
@cInclude("xkbcommon/xkbcommon-compose.h");
|
||||
@cInclude("linux/input-event-codes.h");
|
||||
});
|
||||
|
||||
state: *Core,
|
||||
title: [:0]const u8,
|
||||
size: Core.Size,
|
||||
surface_descriptor: gpu.Surface.Descriptor,
|
||||
configured: bool,
|
||||
decoration: *c.zxdg_toplevel_decoration_v1,
|
||||
|
||||
display: *c.wl_display,
|
||||
surface: *c.wl_surface,
|
||||
registry: *c.struct_wl_registry,
|
||||
xdg_surface: *c.xdg_surface,
|
||||
toplevel: *c.xdg_toplevel,
|
||||
interfaces: Interfaces,
|
||||
libwaylandclient: LibWaylandClient,
|
||||
|
||||
// input stuff
|
||||
keyboard: ?*c.wl_keyboard,
|
||||
pointer: ?*c.wl_pointer,
|
||||
input_state: Core.InputState,
|
||||
|
||||
// keyboard stuff
|
||||
xkb_context: ?*c.xkb_context,
|
||||
xkb_state: ?*c.xkb_state,
|
||||
compose_state: ?*c.xkb_compose_state,
|
||||
keymap: ?*c.xkb_keymap,
|
||||
libxkbcommon: LibXkbCommon,
|
||||
modifiers: Core.KeyMods,
|
||||
modifier_indices: KeyModInd,
|
||||
|
||||
pub fn init(
|
||||
core: *Core.Mod,
|
||||
options: InitOptions,
|
||||
) !Wayland {
|
||||
var wl = undefined;
|
||||
|
||||
wl.state = core.state();
|
||||
|
||||
wl.libxkbcommon = try LibXkbCommon.load();
|
||||
wl.libwaylandclient = try LibWaylandClient.load();
|
||||
wl.interfaces = Interfaces{};
|
||||
|
||||
wl.xkb_context = wl.libxkbcommon.xkb_context_new(0) orelse return error.FailedToGetXkbContext;
|
||||
wl.display = c.wl_display_connect(null) orelse return error.FailedToConnectToWaylandDisplay;
|
||||
wl.registry = c.wl_display_get_registry(wl.display) orelse return error.FailedToGetDisplayRegistry;
|
||||
|
||||
// TODO: handle error return value here
|
||||
_ = c.wl_registry_add_listener(wl.registry, ®istry_listener.listener, Wayland);
|
||||
|
||||
//Round trip to get all the registry objects
|
||||
_ = wl.libwaylandclient.wl_display_roundtrip(wl.display);
|
||||
|
||||
//Round trip to get all initial output events
|
||||
_ = wl.libwaylandclient.wl_display_roundtrip(wl.display);
|
||||
|
||||
//Setup surface
|
||||
wl.surface = c.wl_compositor_create_surface(wl.interfaces.wl_compositor) orelse return error.UnableToCreateSurface;
|
||||
|
||||
{
|
||||
const region = c.wl_compositor_create_region(wl.interfaces.wl_compositor) orelse return error.CouldntCreateWaylandRegtion;
|
||||
|
||||
c.wl_region_add(
|
||||
region,
|
||||
0,
|
||||
0,
|
||||
@intCast(options.size.width),
|
||||
@intCast(options.size.height),
|
||||
);
|
||||
c.wl_surface_set_opaque_region(wl.surface, region);
|
||||
c.wl_region_destroy(region);
|
||||
}
|
||||
|
||||
wl.xdg_surface = c.xdg_wm_base_get_xdg_surface(wl.interfaces.xdg_wm_base, wl.surface) orelse return error.UnableToCreateXdgSurface;
|
||||
wl.toplevel = c.xdg_surface_get_toplevel(wl.xdg_surface) orelse return error.UnableToGetXdgTopLevel;
|
||||
wl.title = try options.allocator.dupeZ(u8, options.title);
|
||||
wl.size = options.size;
|
||||
wl.configured = false; // set by xdg_surface_listener
|
||||
|
||||
// TODO: handle this return value
|
||||
_ = c.xdg_surface_add_listener(wl.xdg_surface, &xdg_surface_listener.listener, wl);
|
||||
|
||||
// TODO: handle this return value
|
||||
_ = c.xdg_toplevel_add_listener(wl.toplevel, &xdg_toplevel_listener.listener, wl);
|
||||
|
||||
// Commit changes to surface
|
||||
c.wl_surface_commit(wl.surface);
|
||||
|
||||
while (wl.libwaylandclient.wl_display_dispatch(wl.display) != -1 and !wl.configured) {
|
||||
// This space intentionally left blank
|
||||
}
|
||||
|
||||
c.xdg_toplevel_set_title(wl.toplevel, wl.title);
|
||||
|
||||
wl.decoration = c.zxdg_decoration_manager_v1_get_toplevel_decoration(
|
||||
wl.interfaces.zxdg_decoration_manager_v1,
|
||||
wl.toplevel,
|
||||
) orelse return error.UnableToGetToplevelDecoration;
|
||||
|
||||
c.zxdg_toplevel_decoration_v1_set_mode(wl.decoration, c.ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
|
||||
|
||||
// Commit changes to surface
|
||||
c.wl_surface_commit(wl.surface);
|
||||
// TODO: handle return value
|
||||
_ = wl.libwaylandclient.wl_display_roundtrip(wl.display);
|
||||
|
||||
// TODO: remove allocation
|
||||
const wayland_surface_descriptor = try options.allocator.create(gpu.Surface.DescriptorFromWaylandSurface);
|
||||
wayland_surface_descriptor.* = .{ .display = wl.display, .surface = wl.surface.? };
|
||||
wl.surface_descriptor = .{ .next_in_chain = .{ .from_wayland_surface = wayland_surface_descriptor } };
|
||||
|
||||
return wl;
|
||||
}
|
||||
|
||||
pub fn deinit(
|
||||
w: *Wayland,
|
||||
linux: *Linux,
|
||||
) void {
|
||||
_ = w;
|
||||
_ = linux;
|
||||
}
|
||||
|
||||
pub fn update(wl: *Wayland) !void {
|
||||
_ = wl.libwaylandclient.wl_display_roundtrip(wl.display);
|
||||
|
||||
wl.core.input.tick();
|
||||
}
|
||||
|
||||
const LibXkbCommon = struct {
|
||||
handle: std.DynLib,
|
||||
|
||||
xkb_context_new: *const @TypeOf(c.xkb_context_new),
|
||||
xkb_keymap_new_from_string: *const @TypeOf(c.xkb_keymap_new_from_string),
|
||||
xkb_state_new: *const @TypeOf(c.xkb_state_new),
|
||||
xkb_keymap_unref: *const @TypeOf(c.xkb_keymap_unref),
|
||||
xkb_state_unref: *const @TypeOf(c.xkb_state_unref),
|
||||
xkb_compose_table_new_from_locale: *const @TypeOf(c.xkb_compose_table_new_from_locale),
|
||||
xkb_compose_state_new: *const @TypeOf(c.xkb_compose_state_new),
|
||||
xkb_compose_table_unref: *const @TypeOf(c.xkb_compose_table_unref),
|
||||
xkb_keymap_mod_get_index: *const @TypeOf(c.xkb_keymap_mod_get_index),
|
||||
xkb_state_update_mask: *const @TypeOf(c.xkb_state_update_mask),
|
||||
xkb_state_mod_index_is_active: *const @TypeOf(c.xkb_state_mod_index_is_active),
|
||||
xkb_state_key_get_syms: *const @TypeOf(c.xkb_state_key_get_syms),
|
||||
xkb_compose_state_feed: *const @TypeOf(c.xkb_compose_state_feed),
|
||||
xkb_compose_state_get_status: *const @TypeOf(c.xkb_compose_state_get_status),
|
||||
xkb_compose_state_get_one_sym: *const @TypeOf(c.xkb_compose_state_get_one_sym),
|
||||
|
||||
pub fn load() !LibXkbCommon {
|
||||
var lib: LibXkbCommon = undefined;
|
||||
lib.handle = std.DynLib.open("libxkbcommon.so.0") catch return error.LibraryNotFound;
|
||||
inline for (@typeInfo(LibXkbCommon).Struct.fields[1..]) |field| {
|
||||
const name = std.fmt.comptimePrint("{s}\x00", .{field.name});
|
||||
const name_z: [:0]const u8 = @ptrCast(name[0 .. name.len - 1]);
|
||||
@field(lib, field.name) = lib.handle.lookup(field.type, name_z) orelse {
|
||||
log.err("Symbol lookup failed for {s}", .{name});
|
||||
return error.SymbolLookup;
|
||||
};
|
||||
}
|
||||
return lib;
|
||||
}
|
||||
};
|
||||
|
||||
const LibWaylandClient = struct {
|
||||
handle: std.DynLib,
|
||||
|
||||
wl_display_connect: *const @TypeOf(c.wl_display_connect),
|
||||
wl_proxy_add_listener: *const @TypeOf(c.wl_proxy_add_listener),
|
||||
wl_proxy_get_version: *const @TypeOf(c.wl_proxy_get_version),
|
||||
wl_proxy_marshal_flags: *const @TypeOf(c.wl_proxy_marshal_flags),
|
||||
wl_proxy_set_tag: *const @TypeOf(c.wl_proxy_set_tag),
|
||||
wl_proxy_destroy: *const @TypeOf(c.wl_proxy_destroy),
|
||||
wl_display_roundtrip: *const @TypeOf(c.wl_display_roundtrip),
|
||||
wl_display_dispatch: *const @TypeOf(c.wl_display_dispatch),
|
||||
wl_display_flush: *const @TypeOf(c.wl_display_flush),
|
||||
wl_display_get_fd: *const @TypeOf(c.wl_display_get_fd),
|
||||
|
||||
//Interfaces
|
||||
wl_compositor_interface: *@TypeOf(c.wl_compositor_interface),
|
||||
wl_subcompositor_interface: *@TypeOf(c.wl_subcompositor_interface),
|
||||
wl_shm_interface: *@TypeOf(c.wl_subcompositor_interface),
|
||||
wl_data_device_manager_interface: *@TypeOf(c.wl_data_device_manager_interface),
|
||||
|
||||
wl_buffer_interface: *@TypeOf(c.wl_buffer_interface),
|
||||
wl_callback_interface: *@TypeOf(c.wl_callback_interface),
|
||||
wl_data_device_interface: *@TypeOf(c.wl_data_device_interface),
|
||||
wl_data_offer_interface: *@TypeOf(c.wl_data_offer_interface),
|
||||
wl_data_source_interface: *@TypeOf(c.wl_data_source_interface),
|
||||
wl_keyboard_interface: *@TypeOf(c.wl_keyboard_interface),
|
||||
wl_output_interface: *@TypeOf(c.wl_output_interface),
|
||||
wl_pointer_interface: *@TypeOf(c.wl_pointer_interface),
|
||||
wl_region_interface: *@TypeOf(c.wl_region_interface),
|
||||
wl_registry_interface: *@TypeOf(c.wl_registry_interface),
|
||||
wl_seat_interface: *@TypeOf(c.wl_seat_interface),
|
||||
wl_shell_surface_interface: *@TypeOf(c.wl_shell_surface_interface),
|
||||
wl_shm_pool_interface: *@TypeOf(c.wl_shm_pool_interface),
|
||||
wl_subsurface_interface: *@TypeOf(c.wl_subsurface_interface),
|
||||
wl_surface_interface: *@TypeOf(c.wl_surface_interface),
|
||||
wl_touch_interface: *@TypeOf(c.wl_touch_interface),
|
||||
|
||||
pub extern const xdg_wm_base_interface: @TypeOf(c.xdg_wm_base_interface);
|
||||
pub extern const zxdg_decoration_manager_v1_interface: @TypeOf(c.zxdg_decoration_manager_v1_interface);
|
||||
|
||||
pub fn load() !LibWaylandClient {
|
||||
var lib: LibWaylandClient = undefined;
|
||||
lib.handle = std.DynLib.open("libwayland-client.so.0") catch return error.LibraryNotFound;
|
||||
inline for (@typeInfo(LibWaylandClient).Struct.fields[1..]) |field| {
|
||||
const name = std.fmt.comptimePrint("{s}\x00", .{field.name});
|
||||
const name_z: [:0]const u8 = @ptrCast(name[0 .. name.len - 1]);
|
||||
@field(lib, field.name) = lib.handle.lookup(field.type, name_z) orelse {
|
||||
log.err("Symbol lookup failed for {s}", .{name});
|
||||
return error.SymbolLookup;
|
||||
};
|
||||
}
|
||||
return lib;
|
||||
}
|
||||
};
|
||||
|
||||
const Interfaces = struct {
|
||||
wl_compositor: ?*c.wl_compositor = null,
|
||||
wl_subcompositor: ?*c.wl_subcompositor = null,
|
||||
wl_shm: ?*c.wl_shm = null,
|
||||
wl_output: ?*c.wl_output = null,
|
||||
wl_seat: ?*c.wl_seat = null,
|
||||
wl_data_device_manager: ?*c.wl_data_device_manager = null,
|
||||
xdg_wm_base: ?*c.xdg_wm_base = null,
|
||||
zxdg_decoration_manager_v1: ?*c.zxdg_decoration_manager_v1 = null,
|
||||
// wp_viewporter: *c.wp_viewporter,
|
||||
// zwp_relative_pointer_manager_v1: *c.zwp_relative_pointer_manager_v1,
|
||||
// zwp_pointer_constraints_v1: *c.zwp_pointer_constraints_v1,
|
||||
// zwp_idle_inhibit_manager_v1: *c.zwp_idle_inhibit_manager_v1,
|
||||
// xdg_activation_v1: *c.xdg_activation_v1,
|
||||
};
|
||||
|
||||
const KeyModInd = struct {
|
||||
control_index: c.xkb_mod_index_t,
|
||||
alt_index: c.xkb_mod_index_t,
|
||||
shift_index: c.xkb_mod_index_t,
|
||||
super_index: c.xkb_mod_index_t,
|
||||
caps_lock_index: c.xkb_mod_index_t,
|
||||
num_lock_index: c.xkb_mod_index_t,
|
||||
};
|
||||
|
||||
const registry_listener = struct {
|
||||
fn registryHandleGlobal(wl: *Wayland, registry: ?*c.struct_wl_registry, name: u32, interface_ptr: [*:0]const u8, version: u32) callconv(.C) void {
|
||||
const interface = std.mem.span(interface_ptr);
|
||||
|
||||
if (std.mem.eql(u8, "wl_compositor", interface)) {
|
||||
wl.interfaces.wl_compositor = @ptrCast(c.wl_registry_bind(
|
||||
registry,
|
||||
name,
|
||||
c.wl_compositor_interface,
|
||||
@min(3, version),
|
||||
) orelse @panic("uh idk how to proceed"));
|
||||
} else if (std.mem.eql(u8, "wl_subcompositor", interface)) {
|
||||
wl.interfaces.wl_subcompositor = @ptrCast(c.wl_registry_bind(
|
||||
registry,
|
||||
name,
|
||||
c.wl_subcompositor_interface,
|
||||
@min(3, version),
|
||||
) orelse @panic("uh idk how to proceed"));
|
||||
} else if (std.mem.eql(u8, "wl_shm", interface)) {
|
||||
wl.interfaces.wl_shm = @ptrCast(c.wl_registry_bind(
|
||||
registry,
|
||||
name,
|
||||
c.wl_shm_interface,
|
||||
@min(3, version),
|
||||
) orelse @panic("uh idk how to proceed"));
|
||||
} else if (std.mem.eql(u8, "wl_output", interface)) {
|
||||
wl.interfaces.wl_output = @ptrCast(c.wl_registry_bind(
|
||||
registry,
|
||||
name,
|
||||
c.wl_output_interface,
|
||||
@min(3, version),
|
||||
) orelse @panic("uh idk how to proceed"));
|
||||
// } else if (std.mem.eql(u8, "wl_data_device_manager", interface)) {
|
||||
// wl.interfaces.wl_data_device_manager = @ptrCast(wl.libwaylandclient.wl_registry_bind(
|
||||
// registry,
|
||||
// name,
|
||||
// wl.libwaylandclient.wl_data_device_manager_interface,
|
||||
// @min(3, version),
|
||||
// ) orelse @panic("uh idk how to proceed"));
|
||||
} else if (std.mem.eql(u8, "xdg_wm_base", interface)) {
|
||||
wl.interfaces.xdg_wm_base = @ptrCast(c.wl_registry_bind(
|
||||
registry,
|
||||
name,
|
||||
c.xdg_wm_base_interface,
|
||||
// &LibWaylandClient._glfw_xdg_wm_base_interface,
|
||||
@min(3, version),
|
||||
) orelse @panic("uh idk how to proceed"));
|
||||
|
||||
// TODO: handle return value
|
||||
_ = c.xdg_wm_base_add_listener(wl.interfaces.xdg_wm_base, &xdg_wm_base_listener.listener, wl);
|
||||
} else if (std.mem.eql(u8, "zxdg_decoration_manager_v1", interface)) {
|
||||
wl.interfaces.zxdg_decoration_manager_v1 = @ptrCast(c.wl_registry_bind(
|
||||
registry,
|
||||
name,
|
||||
c.zxdg_decoration_manager_v1_interface,
|
||||
@min(3, version),
|
||||
) orelse @panic("uh idk how to proceed"));
|
||||
} else if (std.mem.eql(u8, "wl_seat", interface)) {
|
||||
wl.interfaces.wl_seat = @ptrCast(c.wl_registry_bind(
|
||||
registry,
|
||||
name,
|
||||
c.wl_seat_interface,
|
||||
@min(3, version),
|
||||
) orelse @panic("uh idk how to proceed"));
|
||||
|
||||
// TODO: handle return value
|
||||
_ = c.wl_seat_add_listener(wl.interfaces.wl_seat, &seat_listener.listener, wl);
|
||||
}
|
||||
}
|
||||
|
||||
fn registryHandleGlobalRemove(wl: *Wayland, registry: ?*c.struct_wl_registry, name: u32) callconv(.C) void {
|
||||
_ = wl;
|
||||
_ = registry;
|
||||
_ = name;
|
||||
}
|
||||
|
||||
const listener = c.wl_registry_listener{
|
||||
// ptrcast is for the [*:0] -> [*c] conversion, silly yes
|
||||
.global = @ptrCast(®istryHandleGlobal),
|
||||
// ptrcast is for the wl param, which is guarenteed to be our type (and if its not, it should be caught by safety checks)
|
||||
.global_remove = @ptrCast(®istryHandleGlobalRemove),
|
||||
};
|
||||
};
|
||||
|
||||
const keyboard_listener = struct {
|
||||
fn keyboardHandleKeymap(wl: *Wayland, keyboard: ?*c.struct_wl_keyboard, format: u32, fd: i32, keymap_size: u32) callconv(.C) void {
|
||||
_ = keyboard;
|
||||
|
||||
if (format != c.WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
|
||||
@panic("TODO");
|
||||
}
|
||||
|
||||
const map_str = std.posix.mmap(null, keymap_size, std.posix.PROT.READ, .{ .TYPE = .SHARED }, fd, 0) catch unreachable;
|
||||
|
||||
const keymap = c.xkb_keymap_new_from_string(
|
||||
wl.xkb_context,
|
||||
@alignCast(map_str), //align cast happening here, im sure its fine? TODO: figure out if this okay
|
||||
c.XKB_KEYMAP_FORMAT_TEXT_V1,
|
||||
0,
|
||||
).?;
|
||||
|
||||
//Unmap the keymap
|
||||
std.posix.munmap(map_str);
|
||||
//Close the fd
|
||||
std.posix.close(fd);
|
||||
|
||||
const state = c.xkb_state_new(keymap).?;
|
||||
defer c.xkb_state_unref(state);
|
||||
|
||||
//this chain hurts me. why must C be this way.
|
||||
const locale = std.posix.getenv("LC_ALL") orelse std.posix.getenv("LC_CTYPE") orelse std.posix.getenv("LANG") orelse "C";
|
||||
|
||||
var compose_table = wl.libxkbcommon.xkb_compose_table_new_from_locale(
|
||||
wl.xkb_context,
|
||||
locale,
|
||||
c.XKB_COMPOSE_COMPILE_NO_FLAGS,
|
||||
);
|
||||
|
||||
//If creation failed, lets try the C locale
|
||||
if (compose_table == null)
|
||||
compose_table = wl.libxkbcommon.xkb_compose_table_new_from_locale(
|
||||
wl.xkb_context,
|
||||
"C",
|
||||
c.XKB_COMPOSE_COMPILE_NO_FLAGS,
|
||||
).?;
|
||||
|
||||
defer wl.libxkbcommon.xkb_compose_table_unref(compose_table);
|
||||
|
||||
wl.keymap = keymap;
|
||||
wl.xkb_state = state;
|
||||
wl.compose_state = wl.libxkbcommon.xkb_compose_state_new(compose_table, c.XKB_COMPOSE_STATE_NO_FLAGS).?;
|
||||
|
||||
wl.modifier_indices.control_index = wl.libxkbcommon.xkb_keymap_mod_get_index(keymap, "Control");
|
||||
wl.modifier_indices.alt_index = wl.libxkbcommon.xkb_keymap_mod_get_index(keymap, "Mod1");
|
||||
wl.modifier_indices.shift_index = wl.libxkbcommon.xkb_keymap_mod_get_index(keymap, "Shift");
|
||||
wl.modifier_indices.super_index = wl.libxkbcommon.xkb_keymap_mod_get_index(keymap, "Mod4");
|
||||
wl.modifier_indices.caps_lock_index = wl.libxkbcommon.xkb_keymap_mod_get_index(keymap, "Lock");
|
||||
wl.modifier_indices.num_lock_index = wl.libxkbcommon.xkb_keymap_mod_get_index(keymap, "Mod2");
|
||||
}
|
||||
|
||||
fn keyboardHandleEnter(wl: *Wayland, keyboard: ?*c.struct_wl_keyboard, serial: u32, surface: ?*c.struct_wl_surface, keys: [*c]c.struct_wl_array) callconv(.C) void {
|
||||
_ = keyboard;
|
||||
_ = serial;
|
||||
_ = surface;
|
||||
_ = keys;
|
||||
|
||||
wl.state.pushEvent(.focus_gained);
|
||||
}
|
||||
|
||||
fn keyboardHandleLeave(wl: *Wayland, keyboard: ?*c.struct_wl_keyboard, serial: u32, surface: ?*c.struct_wl_surface) callconv(.C) void {
|
||||
_ = keyboard;
|
||||
_ = serial;
|
||||
_ = surface;
|
||||
|
||||
wl.state.pushEvent(.focus_lost);
|
||||
}
|
||||
|
||||
fn keyboardHandleKey(wl: *Wayland, keyboard: ?*c.struct_wl_keyboard, serial: u32, time: u32, scancode: u32, state: u32) callconv(.C) void {
|
||||
_ = keyboard;
|
||||
_ = serial;
|
||||
_ = time;
|
||||
|
||||
const key = toMachKey(scancode);
|
||||
const pressed = state == 1;
|
||||
|
||||
wl.input_state.keys.setValue(@intFromEnum(key), pressed);
|
||||
|
||||
if (pressed) {
|
||||
wl.state.pushEvent(Core.Event{ .key_press = .{
|
||||
.key = key,
|
||||
.mods = wl.modifiers,
|
||||
} });
|
||||
|
||||
var keysyms: ?[*]c.xkb_keysym_t = undefined;
|
||||
//Get the keysym from the keycode (scancode + 8)
|
||||
if (wl.libxkbcommon.xkb_state_key_get_syms(wl.xkb_state, scancode + 8, &keysyms) == 1) {
|
||||
//Compose the keysym
|
||||
const keysym: c.xkb_keysym_t = composeSymbol(wl, keysyms.?[0]);
|
||||
|
||||
//Try to convert that keysym to a unicode codepoint
|
||||
if (Linux.unicodeFromKeySym(keysym)) |codepoint| {
|
||||
wl.state.pushEvent(Core.Event{ .char_input = .{ .codepoint = codepoint } });
|
||||
}
|
||||
}
|
||||
} else {
|
||||
wl.state.pushEvent(Core.Event{ .key_release = .{
|
||||
.key = key,
|
||||
.mods = wl.modifiers,
|
||||
} });
|
||||
}
|
||||
}
|
||||
|
||||
fn keyboardHandleModifiers(wl: *Wayland, keyboard: ?*c.struct_wl_keyboard, serial: u32, mods_depressed: u32, mods_latched: u32, mods_locked: u32, group: u32) callconv(.C) void {
|
||||
_ = keyboard;
|
||||
_ = serial;
|
||||
|
||||
if (wl.keymap == null)
|
||||
return;
|
||||
|
||||
// TODO: handle this return value
|
||||
_ = wl.libxkbcommon.xkb_state_update_mask(
|
||||
wl.xkb_state.?,
|
||||
mods_depressed,
|
||||
mods_latched,
|
||||
mods_locked,
|
||||
0,
|
||||
0,
|
||||
group,
|
||||
);
|
||||
|
||||
//Iterate over all the modifiers
|
||||
inline for (.{
|
||||
.{ wl.modifier_indices.alt_index, "alt" },
|
||||
.{ wl.modifier_indices.shift_index, "shift" },
|
||||
.{ wl.modifier_indices.super_index, "super" },
|
||||
.{ wl.modifier_indices.control_index, "control" },
|
||||
.{ wl.modifier_indices.num_lock_index, "num_lock" },
|
||||
.{ wl.modifier_indices.caps_lock_index, "caps_lock" },
|
||||
}) |key| {
|
||||
@field(wl.modifiers, key[1]) = wl.libxkbcommon.xkb_state_mod_index_is_active(
|
||||
wl.xkb_state,
|
||||
key[0],
|
||||
c.XKB_STATE_MODS_EFFECTIVE,
|
||||
) == 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn keyboardHandleRepeatInfo(wl: *Wayland, keyboard: ?*c.struct_wl_keyboard, rate: i32, delay: i32) callconv(.C) void {
|
||||
_ = wl;
|
||||
_ = keyboard;
|
||||
_ = rate;
|
||||
_ = delay;
|
||||
}
|
||||
|
||||
const listener = c.wl_keyboard_listener{
|
||||
.keymap = @ptrCast(&keyboardHandleKeymap),
|
||||
.enter = @ptrCast(&keyboardHandleEnter),
|
||||
.leave = @ptrCast(&keyboardHandleLeave),
|
||||
.key = @ptrCast(&keyboardHandleKey),
|
||||
.modifiers = @ptrCast(&keyboardHandleModifiers),
|
||||
.repeat_info = @ptrCast(&keyboardHandleRepeatInfo),
|
||||
};
|
||||
};
|
||||
|
||||
const pointer_listener = struct {
|
||||
fn handlePointerAxis(wl: *Wayland, pointer: ?*c.struct_wl_pointer, time: u32, axis: u32, value: c.wl_fixed_t) callconv(.C) void {
|
||||
_ = wl;
|
||||
_ = pointer;
|
||||
_ = time;
|
||||
_ = axis;
|
||||
_ = value;
|
||||
}
|
||||
|
||||
fn handlePointerFrame(wl: *Wayland, pointer: ?*c.struct_wl_pointer) callconv(.C) void {
|
||||
_ = wl;
|
||||
_ = pointer;
|
||||
}
|
||||
|
||||
fn handlePointerAxisSource(wl: *Wayland, pointer: ?*c.struct_wl_pointer, axis_source: u32) callconv(.C) void {
|
||||
_ = wl;
|
||||
_ = pointer;
|
||||
_ = axis_source;
|
||||
}
|
||||
|
||||
fn handlePointerAxisStop(wl: *Wayland, pointer: ?*c.struct_wl_pointer, time: u32, axis: u32) callconv(.C) void {
|
||||
_ = wl;
|
||||
_ = pointer;
|
||||
_ = time;
|
||||
_ = axis;
|
||||
}
|
||||
|
||||
fn handlePointerAxisDiscrete(wl: *Wayland, pointer: ?*c.struct_wl_pointer, axis: u32, discrete: i32) callconv(.C) void {
|
||||
_ = wl;
|
||||
_ = pointer;
|
||||
_ = axis;
|
||||
_ = discrete;
|
||||
}
|
||||
|
||||
fn handlePointerAxisValue120(wl: *Wayland, pointer: ?*c.struct_wl_pointer, axis: u32, value_120: i32) callconv(.C) void {
|
||||
_ = wl;
|
||||
_ = pointer;
|
||||
_ = axis;
|
||||
_ = value_120;
|
||||
}
|
||||
|
||||
fn handlePointerAxisRelativeDirection(wl: *Wayland, pointer: ?*c.struct_wl_pointer, axis: u32, direction: u32) callconv(.C) void {
|
||||
_ = wl;
|
||||
_ = pointer;
|
||||
_ = axis;
|
||||
_ = direction;
|
||||
}
|
||||
|
||||
fn handlePointerEnter(wl: *Wayland, pointer: ?*c.struct_wl_pointer, serial: u32, surface: ?*c.struct_wl_surface, fixed_x: c.wl_fixed_t, fixed_y: c.wl_fixed_t) callconv(.C) void {
|
||||
_ = fixed_x;
|
||||
_ = fixed_y;
|
||||
_ = wl;
|
||||
_ = pointer;
|
||||
_ = serial;
|
||||
_ = surface;
|
||||
}
|
||||
|
||||
fn handlePointerLeave(wl: *Wayland, pointer: ?*c.struct_wl_pointer, serial: u32, surface: ?*c.struct_wl_surface) callconv(.C) void {
|
||||
_ = wl;
|
||||
_ = pointer;
|
||||
_ = serial;
|
||||
_ = surface;
|
||||
}
|
||||
|
||||
fn handlePointerMotion(wl: *Wayland, pointer: ?*c.struct_wl_pointer, serial: u32, fixed_x: c.wl_fixed_t, fixed_y: c.wl_fixed_t) callconv(.C) void {
|
||||
_ = pointer;
|
||||
_ = serial;
|
||||
|
||||
const x = c.wl_fixed_to_double(fixed_x);
|
||||
const y = c.wl_fixed_to_double(fixed_y);
|
||||
|
||||
wl.state.pushEvent(.{ .mouse_motion = .{ .pos = .{ .x = x, .y = y } } });
|
||||
wl.input_state.mouse_position = .{ .x = x, .y = y };
|
||||
}
|
||||
|
||||
fn handlePointerButton(wl: *Wayland, pointer: ?*c.struct_wl_pointer, serial: u32, time: u32, button: u32, state: u32) callconv(.C) void {
|
||||
_ = pointer;
|
||||
_ = serial;
|
||||
_ = time;
|
||||
|
||||
const mouse_button: Core.MouseButton = @enumFromInt(button - c.BTN_LEFT);
|
||||
const pressed = state == c.WL_POINTER_BUTTON_STATE_PRESSED;
|
||||
|
||||
wl.input_state.mouse_buttons.setValue(@intFromEnum(mouse_button), pressed);
|
||||
|
||||
if (pressed) {
|
||||
wl.state.pushEvent(Core.Event{ .mouse_press = .{
|
||||
.button = mouse_button,
|
||||
.mods = wl.modifiers,
|
||||
.pos = wl.input_state.mouse_position,
|
||||
} });
|
||||
} else {
|
||||
wl.state.pushEvent(Core.Event{ .mouse_release = .{
|
||||
.button = mouse_button,
|
||||
.mods = wl.modifiers,
|
||||
.pos = wl.input_state.mouse_position,
|
||||
} });
|
||||
}
|
||||
}
|
||||
|
||||
const listener = c.wl_pointer_listener{
|
||||
.axis = @ptrCast(&handlePointerAxis),
|
||||
.axis_discrete = @ptrCast(&handlePointerAxisDiscrete),
|
||||
.axis_relative_direction = @ptrCast(&handlePointerAxisRelativeDirection),
|
||||
.axis_source = @ptrCast(&handlePointerAxisSource),
|
||||
.axis_stop = @ptrCast(&handlePointerAxisStop),
|
||||
.axis_value120 = @ptrCast(&handlePointerAxisValue120),
|
||||
.button = @ptrCast(&handlePointerButton),
|
||||
.enter = @ptrCast(&handlePointerEnter),
|
||||
.frame = @ptrCast(&handlePointerFrame),
|
||||
.leave = @ptrCast(&handlePointerLeave),
|
||||
.motion = @ptrCast(&handlePointerMotion),
|
||||
};
|
||||
};
|
||||
|
||||
const seat_listener = struct {
|
||||
fn seatHandleName(wl: *Wayland, seat: ?*c.struct_wl_seat, name_ptr: [*:0]const u8) callconv(.C) void {
|
||||
_ = wl;
|
||||
_ = seat;
|
||||
_ = name_ptr;
|
||||
}
|
||||
|
||||
fn seatHandleCapabilities(wl: *Wayland, seat: ?*c.struct_wl_seat, caps: c.wl_seat_capability) callconv(.C) void {
|
||||
if ((caps & c.WL_SEAT_CAPABILITY_KEYBOARD) != 0) {
|
||||
wl.keyboard = c.wl_seat_get_keyboard(seat);
|
||||
|
||||
// TODO: handle return value
|
||||
_ = c.wl_keyboard_add_listener(wl.keyboard, &keyboard_listener.listener, wl);
|
||||
}
|
||||
|
||||
if ((caps & c.WL_SEAT_CAPABILITY_TOUCH) != 0) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
if ((caps & c.WL_SEAT_CAPABILITY_POINTER) != 0) {
|
||||
wl.pointer = c.wl_seat_get_pointer(seat);
|
||||
|
||||
// TODO: handle return value
|
||||
_ = c.wl_pointer_add_listener(wl.pointer, &pointer_listener.listener, wl);
|
||||
}
|
||||
|
||||
// Delete keyboard if its no longer in the seat
|
||||
if (wl.keyboard) |keyboard| {
|
||||
if ((caps & c.WL_SEAT_CAPABILITY_KEYBOARD) == 0) {
|
||||
c.wl_keyboard_destroy(keyboard);
|
||||
wl.keyboard = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (wl.pointer) |pointer| {
|
||||
if ((caps & c.WL_SEAT_CAPABILITY_POINTER) == 0) {
|
||||
c.wl_pointer_destroy(pointer);
|
||||
wl.pointer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const listener = c.wl_seat_listener{
|
||||
.capabilities = @ptrCast(&seatHandleCapabilities),
|
||||
.name = @ptrCast(&seatHandleName), //ptrCast for the `[*:0]const u8`
|
||||
};
|
||||
};
|
||||
|
||||
const xdg_wm_base_listener = struct {
|
||||
fn wmBaseHandlePing(wl: *Wayland, wm_base: ?*c.struct_xdg_wm_base, serial: u32) callconv(.C) void {
|
||||
_ = wl;
|
||||
c.xdg_wm_base_pong(wm_base, serial);
|
||||
}
|
||||
|
||||
const listener = c.xdg_wm_base_listener{ .ping = @ptrCast(&wmBaseHandlePing) };
|
||||
};
|
||||
|
||||
const xdg_surface_listener = struct {
|
||||
fn xdgSurfaceHandleConfigure(wl: *Wayland, xdg_surface: ?*c.struct_xdg_surface, serial: u32) callconv(.C) void {
|
||||
c.xdg_surface_ack_configure(xdg_surface, serial);
|
||||
|
||||
if (wl.configured) {
|
||||
c.wl_surface_commit(wl.surface);
|
||||
} else {
|
||||
wl.configured = true;
|
||||
}
|
||||
|
||||
setContentAreaOpaque(wl, wl.size);
|
||||
}
|
||||
|
||||
const listener = c.wl_surface_listener{ .configure = @ptrCast(&xdgSurfaceHandleConfigure) };
|
||||
};
|
||||
|
||||
const xdg_toplevel_listener = struct {
|
||||
fn xdgToplevelHandleClose(wl: *Wayland, toplevel: ?*c.struct_xdg_toplevel) callconv(.C) void {
|
||||
_ = wl;
|
||||
_ = toplevel;
|
||||
}
|
||||
|
||||
fn xdgToplevelHandleConfigure(wl: *Wayland, toplevel: ?*c.struct_xdg_toplevel, width: i32, height: i32, states: [*c]c.struct_wl_array) callconv(.C) void {
|
||||
_ = toplevel;
|
||||
_ = states;
|
||||
|
||||
if (width > 0 and height > 0) {
|
||||
wl.size = .{ .width = @intCast(width), .height = @intCast(height) };
|
||||
}
|
||||
}
|
||||
|
||||
const listener = c.xdg_toplevel_listener{
|
||||
.configure = @ptrCast(&xdgToplevelHandleConfigure),
|
||||
.close = @ptrCast(&xdgToplevelHandleClose),
|
||||
};
|
||||
};
|
||||
|
||||
fn composeSymbol(wl: *Wayland, sym: c.xkb_keysym_t) c.xkb_keysym_t {
|
||||
if (sym == c.XKB_KEY_NoSymbol or wl.compose_state == null)
|
||||
return sym;
|
||||
|
||||
if (wl.libxkbcommon.xkb_compose_state_feed(wl.compose_state, sym) != c.XKB_COMPOSE_FEED_ACCEPTED)
|
||||
return sym;
|
||||
|
||||
return switch (wl.libxkbcommon.xkb_compose_state_get_status(wl.compose_state)) {
|
||||
c.XKB_COMPOSE_COMPOSED => wl.libxkbcommon.xkb_compose_state_get_one_sym(wl.compose_state),
|
||||
c.XKB_COMPOSE_COMPOSING, c.XKB_COMPOSE_CANCELLED => c.XKB_KEY_NoSymbol,
|
||||
else => sym,
|
||||
};
|
||||
}
|
||||
|
||||
fn toMachKey(key: u32) Core.Key {
|
||||
return switch (key) {
|
||||
c.KEY_GRAVE => .grave,
|
||||
c.KEY_1 => .one,
|
||||
c.KEY_2 => .two,
|
||||
c.KEY_3 => .three,
|
||||
c.KEY_4 => .four,
|
||||
c.KEY_5 => .five,
|
||||
c.KEY_6 => .six,
|
||||
c.KEY_7 => .seven,
|
||||
c.KEY_8 => .eight,
|
||||
c.KEY_9 => .nine,
|
||||
c.KEY_0 => .zero,
|
||||
c.KEY_SPACE => .space,
|
||||
c.KEY_MINUS => .minus,
|
||||
c.KEY_EQUAL => .equal,
|
||||
c.KEY_Q => .q,
|
||||
c.KEY_W => .w,
|
||||
c.KEY_E => .e,
|
||||
c.KEY_R => .r,
|
||||
c.KEY_T => .t,
|
||||
c.KEY_Y => .y,
|
||||
c.KEY_U => .u,
|
||||
c.KEY_I => .i,
|
||||
c.KEY_O => .o,
|
||||
c.KEY_P => .p,
|
||||
c.KEY_LEFTBRACE => .left_bracket,
|
||||
c.KEY_RIGHTBRACE => .right_bracket,
|
||||
c.KEY_A => .a,
|
||||
c.KEY_S => .s,
|
||||
c.KEY_D => .d,
|
||||
c.KEY_F => .f,
|
||||
c.KEY_G => .g,
|
||||
c.KEY_H => .h,
|
||||
c.KEY_J => .j,
|
||||
c.KEY_K => .k,
|
||||
c.KEY_L => .l,
|
||||
c.KEY_SEMICOLON => .semicolon,
|
||||
c.KEY_APOSTROPHE => .apostrophe,
|
||||
c.KEY_Z => .z,
|
||||
c.KEY_X => .x,
|
||||
c.KEY_C => .c,
|
||||
c.KEY_V => .v,
|
||||
c.KEY_B => .b,
|
||||
c.KEY_N => .n,
|
||||
c.KEY_M => .m,
|
||||
c.KEY_COMMA => .comma,
|
||||
c.KEY_DOT => .period,
|
||||
c.KEY_SLASH => .slash,
|
||||
c.KEY_BACKSLASH => .backslash,
|
||||
c.KEY_ESC => .escape,
|
||||
c.KEY_TAB => .tab,
|
||||
c.KEY_LEFTSHIFT => .left_shift,
|
||||
c.KEY_RIGHTSHIFT => .right_shift,
|
||||
c.KEY_LEFTCTRL => .left_control,
|
||||
c.KEY_RIGHTCTRL => .right_control,
|
||||
c.KEY_LEFTALT => .left_alt,
|
||||
c.KEY_RIGHTALT => .right_alt,
|
||||
c.KEY_LEFTMETA => .left_super,
|
||||
c.KEY_RIGHTMETA => .right_super,
|
||||
c.KEY_NUMLOCK => .num_lock,
|
||||
c.KEY_CAPSLOCK => .caps_lock,
|
||||
c.KEY_PRINT => .print,
|
||||
c.KEY_SCROLLLOCK => .scroll_lock,
|
||||
c.KEY_PAUSE => .pause,
|
||||
c.KEY_DELETE => .delete,
|
||||
c.KEY_BACKSPACE => .backspace,
|
||||
c.KEY_ENTER => .enter,
|
||||
c.KEY_HOME => .home,
|
||||
c.KEY_END => .end,
|
||||
c.KEY_PAGEUP => .page_up,
|
||||
c.KEY_PAGEDOWN => .page_down,
|
||||
c.KEY_INSERT => .insert,
|
||||
c.KEY_LEFT => .left,
|
||||
c.KEY_RIGHT => .right,
|
||||
c.KEY_DOWN => .down,
|
||||
c.KEY_UP => .up,
|
||||
c.KEY_F1 => .f1,
|
||||
c.KEY_F2 => .f2,
|
||||
c.KEY_F3 => .f3,
|
||||
c.KEY_F4 => .f4,
|
||||
c.KEY_F5 => .f5,
|
||||
c.KEY_F6 => .f6,
|
||||
c.KEY_F7 => .f7,
|
||||
c.KEY_F8 => .f8,
|
||||
c.KEY_F9 => .f9,
|
||||
c.KEY_F10 => .f10,
|
||||
c.KEY_F11 => .f11,
|
||||
c.KEY_F12 => .f12,
|
||||
c.KEY_F13 => .f13,
|
||||
c.KEY_F14 => .f14,
|
||||
c.KEY_F15 => .f15,
|
||||
c.KEY_F16 => .f16,
|
||||
c.KEY_F17 => .f17,
|
||||
c.KEY_F18 => .f18,
|
||||
c.KEY_F19 => .f19,
|
||||
c.KEY_F20 => .f20,
|
||||
c.KEY_F21 => .f21,
|
||||
c.KEY_F22 => .f22,
|
||||
c.KEY_F23 => .f23,
|
||||
c.KEY_F24 => .f24,
|
||||
c.KEY_KPSLASH => .kp_divide,
|
||||
c.KEY_KPASTERISK => .kp_multiply,
|
||||
c.KEY_KPMINUS => .kp_subtract,
|
||||
c.KEY_KPPLUS => .kp_add,
|
||||
c.KEY_KP0 => .kp_0,
|
||||
c.KEY_KP1 => .kp_1,
|
||||
c.KEY_KP2 => .kp_2,
|
||||
c.KEY_KP3 => .kp_3,
|
||||
c.KEY_KP4 => .kp_4,
|
||||
c.KEY_KP5 => .kp_5,
|
||||
c.KEY_KP6 => .kp_6,
|
||||
c.KEY_KP7 => .kp_7,
|
||||
c.KEY_KP8 => .kp_8,
|
||||
c.KEY_KP9 => .kp_9,
|
||||
c.KEY_KPDOT => .kp_decimal,
|
||||
c.KEY_KPEQUAL => .kp_equal,
|
||||
c.KEY_KPENTER => .kp_enter,
|
||||
else => .unknown,
|
||||
};
|
||||
}
|
||||
|
||||
fn setContentAreaOpaque(wl: *Wayland, new_size: Core.Size) void {
|
||||
const region = c.wl_compositor_create_region(wl.interfaces.wl_compositor) orelse return;
|
||||
|
||||
c.wl_region_add(region, 0, 0, @intCast(new_size.width), @intCast(new_size.height));
|
||||
c.wl_surface_set_opaque_region(wl.surface, region);
|
||||
c.wl_region_destroy(region);
|
||||
|
||||
wl.core.swap_chain_update.set();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue