Mezzaluna/src/Seat.zig
2025-12-18 17:23:00 -05:00

173 lines
5.1 KiB
Zig

const Seat = @This();
const std = @import("std");
const wlr = @import("wlroots");
const wl = @import("wayland").server.wl;
const xkb = @import("xkbcommon");
const Utils = @import("Utils.zig");
const Popup = @import("Popup.zig");
const View = @import("View.zig");
const LayerSurface = @import("LayerSurface.zig");
const Output = @import("Output.zig");
const server = &@import("main.zig").server;
pub const FocusData = union(enum) {
view: *View,
layer_surface: *LayerSurface,
pub fn getSurface(self: FocusData) *wlr.Surface {
return switch (self) {
.view => |*v| v.*.xdg_toplevel.base.surface,
.layer_surface => |*ls| ls.*.wlr_layer_surface.surface,
};
}
};
wlr_seat: *wlr.Seat,
focused_surface: ?FocusData,
focused_output: ?*Output,
keyboard_group: *wlr.KeyboardGroup,
keymap: *xkb.Keymap,
request_set_cursor: wl.Listener(*wlr.Seat.event.RequestSetCursor) = .init(handleRequestSetCursor),
request_set_selection: wl.Listener(*wlr.Seat.event.RequestSetSelection) = .init(handleRequestSetSelection),
// request_set_primary_selection
// request_start_drage
pub fn init(self: *Seat) void {
errdefer Utils.oomPanic();
const xkb_context = xkb.Context.new(.no_flags) orelse {
std.log.err("Unable to create a xkb context, exiting", .{});
std.process.exit(7);
};
defer xkb_context.unref();
const keymap = xkb.Keymap.newFromNames(xkb_context, null, .no_flags) orelse {
std.log.err("Unable to create a xkb keymap, exiting", .{});
std.process.exit(8);
};
defer keymap.unref();
self.* = .{
.wlr_seat = try wlr.Seat.create(server.wl_server, "default"),
.focused_surface = null,
.focused_output = null,
.keyboard_group = try wlr.KeyboardGroup.create(),
.keymap = keymap.ref(),
};
errdefer {
self.keyboard_group.destroy();
self.wlr_seat.destroy();
}
_ = self.keyboard_group.keyboard.setKeymap(self.keymap);
self.wlr_seat.setKeyboard(&self.keyboard_group.keyboard);
self.wlr_seat.events.request_set_cursor.add(&self.request_set_cursor);
self.wlr_seat.events.request_set_selection.add(&self.request_set_selection);
}
pub fn deinit(self: *Seat) void {
self.request_set_cursor.link.remove();
self.request_set_selection.link.remove();
self.keyboard_group.destroy();
self.wlr_seat.destroy();
}
pub fn focusSurface(self: *Seat, to_focus: ?FocusData) void {
const surface: ?*wlr.Surface = blk: {
if (to_focus != null) {
break :blk to_focus.?.getSurface();
} else {
break :blk null;
}
};
// Remove focus from the current surface unless:
// - current and to focus are the same surface
// - current layer has exclusive keyboard interactivity
// - current is fullscreen and to focus is content
// - current is fullscreen and layer is bottom or background
if (self.focused_surface) |current_focus| {
const current_surface = current_focus.getSurface();
if (current_surface == surface) return; // Same surface
if(to_focus != null) {
switch (current_focus) {
.layer_surface => |*current_layer_surface| {
if(current_layer_surface.*.wlr_layer_surface.current.keyboard_interactive == .exclusive) return;
},
.view => |*current_view| {
if(current_view.*.fullscreen) {
switch (to_focus.?) {
.layer_surface => |*layer_surface| {
const layer = layer_surface.*.wlr_layer_surface.current.layer;
if(layer == .background or layer == .bottom) return;
},
.view => |*view| {
if(!view.*.fullscreen) return;
}
}
}
}
}
} else if(current_focus == .view and current_focus.view.fullscreen){
return;
}
// Clear the focus if applicable
switch (current_focus) {
.layer_surface => {}, // IDK if we actually have to clear any focus here
else => {
if(wlr.XdgSurface.tryFromWlrSurface(current_surface)) |xdg_surface| {
_ = xdg_surface.role_data.toplevel.?.setActivated(false);
}
}
}
}
if(to_focus != null) {
server.seat.wlr_seat.keyboardNotifyEnter(
surface.?,
&server.seat.wlr_seat.keyboard_state.keyboard.?.keycodes,
null
);
if(to_focus.? != .layer_surface) {
if(to_focus.? == .view) to_focus.?.view.focused = true;
if(wlr.XdgSurface.tryFromWlrSurface(surface.?)) |xdg_surface| {
_ = xdg_surface.role_data.toplevel.?.setActivated(true);
}
}
}
self.focused_surface = to_focus;
}
pub fn focusOutput(self: *Seat, output: *Output) void {
if(server.seat.focused_output) |prev_output| {
prev_output.focused = false;
}
self.focused_output = output;
}
fn handleRequestSetCursor(
_: *wl.Listener(*wlr.Seat.event.RequestSetCursor),
event: *wlr.Seat.event.RequestSetCursor,
) void {
if (event.seat_client == server.seat.wlr_seat.pointer_state.focused_client)
server.cursor.wlr_cursor.setSurface(event.surface, event.hotspot_x, event.hotspot_y);
}
fn handleRequestSetSelection (
_: *wl.Listener(*wlr.Seat.event.RequestSetSelection),
event: *wlr.Seat.event.RequestSetSelection,
) void {
server.seat.wlr_seat.setSelection(event.source, event.serial);
}