focusing of surfaces or more streamlined

This commit is contained in:
Harrison DiAmbrosio 2025-12-15 20:00:49 -05:00
parent 4b3e1bbd5d
commit a90c106b8c
8 changed files with 171 additions and 107 deletions

View file

@ -295,11 +295,7 @@ local master = function()
})
local fullscreen = function (view_id)
print("Fullscreen")
mez.view.toggle_fullscreen(view_id)
mez.view.set_position(view_id, 0, 0)
local res = mez.output.get_resolution(0)
mez.view.set_size(view_id, res.width, res.height)
tile_all()
end

View file

@ -122,15 +122,18 @@ pub fn processCursorMotion(self: *Cursor, time_msec: u32) void {
},
.resize => {
// Fix this resize
const focused_view = server.seat.focused_view;
const view: *View = blk: {
if(server.seat.focused_surface) |fs| {
if(fs != .view) return;
break :blk fs.view;
} else { return; }
};
if(focused_view) |view| {
_ = view.xdg_toplevel.setSize(
// TODO: configure the min and max using lua?
std.math.clamp(@as(c_int, @as(i32, @intFromFloat(self.wlr_cursor.x)) - view.scene_tree.node.x), 10, std.math.maxInt(i32)),
std.math.clamp(@as(c_int, @as(i32, @intFromFloat(self.wlr_cursor.y)) - view.scene_tree.node.y), 10, std.math.maxInt(i32))
);
}
},
}
}
@ -160,10 +163,6 @@ fn handleButton(
_ = server.seat.wlr_seat.pointerNotifyButton(event.time_msec, event.button, event.state);
if (server.seat.focused_view) |view| {
view.setFocused();
}
// @hook PointerButtonPress // TODO Probably change this name
// @param button string // TODO Translate a button to a string or smth
// @param state string - "pressed" or "released"
@ -177,18 +176,23 @@ fn handleButton(
// Can be BTN_RIGHT, BTN_LEFT, or BTN_MIDDLE
cursor.drag.start_x = @as(c_int, @intFromFloat(cursor.wlr_cursor.x));
cursor.drag.start_y = @as(c_int, @intFromFloat(cursor.wlr_cursor.y));
if(server.seat.focused_view) |view| {
if(server.seat.focused_surface) |fs| {
// Keep track of where the drag started
cursor.drag.view = view;
cursor.drag.view_offset_x = cursor.drag.start_x - view.scene_tree.node.x;
cursor.drag.view_offset_y = cursor.drag.start_y - view.scene_tree.node.y;
if(fs == .view) {
cursor.drag.view = fs.view;
cursor.drag.view_offset_x = cursor.drag.start_x - fs.view.scene_tree.node.x;
cursor.drag.view_offset_y = cursor.drag.start_y - fs.view.scene_tree.node.y;
}
// Maybe comptime this for later reference
if(event.button == c.libevdev_event_code_from_name(c.EV_KEY, "BTN_LEFT")) {
cursor.mode = .move;
} else if(event.button == c.libevdev_event_code_from_name(c.EV_KEY, "BTN_RIGHT")) {
if(fs == .view) {
cursor.mode = .resize;
_ = view.xdg_toplevel.setResizing(true);
_ = fs.view.xdg_toplevel.setResizing(true);
}
}
}
}

View file

@ -19,6 +19,7 @@ const server = &@import("main.zig").server;
focused: bool,
id: u64,
fullscreen: ?*View,
wlr_output: *wlr.Output,
state: wlr.Output.State,
@ -63,6 +64,7 @@ pub fn init(wlr_output: *wlr.Output) ?*Output {
.id = @intFromPtr(wlr_output),
.wlr_output = wlr_output,
.tree = try server.root.scene.tree.createSceneTree(),
.fullscreen = null,
.layers = .{
.background = try self.tree.createSceneTree(),
.bottom = try self.tree.createSceneTree(),
@ -84,8 +86,6 @@ pub fn init(wlr_output: *wlr.Output) ?*Output {
.state = wlr.Output.State.init()
};
wlr_output.events.frame.add(&self.frame);
wlr_output.events.request_state.add(&self.request_state);
wlr_output.events.destroy.add(&self.destroy);
@ -259,6 +259,21 @@ pub fn surfaceAt(self: *Output, lx: f64, ly: f64) ?SurfaceAtResult {
return null;
}
pub fn getFullscreenedView(self: *Output) ?*View {
if(self.layers.fullscreen.children.length() != 1) {
return null;
}
var it = self.layers.fullscreen.children.iterator(.forward);
if(it.next().?.data) |data| {
const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(data));
if(scene_node_data.* == .view) {
return scene_node_data.view;
}
}
return null;
}
// --------- WlrOutput Event Handlers ---------
fn handleRequestState(
listener: *wl.Listener(*wlr.Output.event.RequestState),

View file

@ -6,13 +6,23 @@ 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;
const FocusDataType = enum { view, popup, layer_surface };
pub const FocusData = union(FocusDataType) {
view: *View,
popup: *Popup,
layer_surface: *LayerSurface
};
wlr_seat: *wlr.Seat,
focused_view: ?*View,
focused_surface: ?FocusData,
focused_output: ?*Output,
keyboard_group: *wlr.KeyboardGroup,
@ -40,7 +50,7 @@ pub fn init(self: *Seat) void {
self.* = .{
.wlr_seat = try wlr.Seat.create(server.wl_server, "default"),
.focused_view = null,
.focused_surface = null,
.focused_output = null,
.keyboard_group = try wlr.KeyboardGroup.create(),
.keymap = keymap.ref(),
@ -65,6 +75,74 @@ pub fn deinit(self: *Seat) void {
self.wlr_seat.destroy();
}
pub fn focusSurface(self: *Seat, to_focus: ?FocusData) void {
const surface: ?*wlr.Surface = blk: {
if (to_focus != null) {
break :blk switch (to_focus.?) {
.view => to_focus.?.view.xdg_toplevel.base.surface,
.layer_surface => to_focus.?.layer_surface.wlr_layer_surface.surface,
.popup => to_focus.?.popup.xdg_popup.base.surface
};
} else {
break :blk null;
}
};
// Remove focus from the current surface unless
// - current has exclusive keyboard interactivity
// - the current view is fullscreen and
// - to focus is a content level view
// - to focus is a bottom or background level layer
if (self.focused_surface) |current_focus| {
const current_surface = switch(current_focus) {
.view => current_focus.view.xdg_toplevel.base.surface,
.layer_surface => current_focus.layer_surface.wlr_layer_surface.surface,
.popup => current_focus.popup.xdg_popup.base.surface
};
if (current_surface == surface) return; // Same surface
// Don't change focus under these circumstances
if(current_focus == .layer_surface) {
if(current_focus.layer_surface.wlr_layer_surface.current.keyboard_interactive == .exclusive) return;
} else if(current_focus == .view and current_focus.view.fullscreen) { // Current focus is a fullscreened view
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; // TODO: Is this the correct conditional
},
else => {}
}
}
// 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(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;

View file

@ -239,7 +239,7 @@ fn handleRequestActivate(
const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(event.surface.data.?));
if(scene_node_data.* == .view) {
scene_node_data.view.setFocused();
server.seat.focusSurface(Seat.FocusData{ .view = scene_node_data.view });
} else {
std.log.warn("Ignoring request to activate non-view", .{});
}

View file

@ -104,53 +104,34 @@ pub fn deinit(self: *View) void {
self.request_resize.link.remove();
}
pub fn setFocused(self: *View) void {
if (server.seat.wlr_seat.keyboard_state.focused_surface) |previous_surface| {
if (previous_surface == self.xdg_toplevel.base.surface) return;
if (wlr.XdgSurface.tryFromWlrSurface(previous_surface)) |xdg_surface| {
_ = xdg_surface.role_data.toplevel.?.setActivated(false);
}
}
self.scene_tree.node.raiseToTop();
_ = self.xdg_toplevel.setActivated(true);
const wlr_keyboard = server.seat.wlr_seat.getKeyboard() orelse return;
server.seat.wlr_seat.keyboardNotifyEnter(
self.xdg_toplevel.base.surface,
wlr_keyboard.keycodes[0..wlr_keyboard.num_keycodes],
&wlr_keyboard.modifiers,
);
if(server.seat.focused_view) |prev_view| {
prev_view.focused = false;
}
server.seat.focused_view = self;
self.focused = true;
}
pub fn close(self: *View) void {
self.xdg_toplevel.sendClose();
if(self.focused) {
server.seat.focused_view = null;
self.focused = false;
_ = self.xdg_toplevel.base.role_data.toplevel.?.setActivated(false);
server.seat.focused_surface = null;
}
}
pub fn toFullscreen(self: *View) void {
// TODO: What should the final behaviour of this be
pub fn toggleFullscreen(self: *View) void {
self.fullscreen = !self.fullscreen;
if(server.seat.focused_output) |output| {
if(self.fullscreen and output.fullscreen != self) {
// Check to see if another fullscreened view exists, if so replace it
if(output.getFullscreenedView()) |view| {
view.toggleFullscreen();
}
self.scene_tree.node.reparent(output.layers.fullscreen);
}
self.fullscreen = true;
_ = self.xdg_toplevel.setFullscreen(true);
}
pub fn toContent(self: *View) void {
if(server.seat.focused_output) |output| {
self.setPosition(0, 0);
self.setSize(output.wlr_output.width, output.wlr_output.height);
output.fullscreen = self;
} else {
self.scene_tree.node.reparent(output.layers.content);
output.fullscreen = null;
}
self.fullscreen = false;
_ = self.xdg_toplevel.setFullscreen(false);
}
_ = self.xdg_toplevel.setFullscreen(self.fullscreen);
}
pub fn setPosition(self: *View, x: i32, y: i32) void {

View file

@ -3,6 +3,10 @@ const LuaUtils = @This();
const std = @import("std");
const zlua = @import("zlua");
const View = @import("../View.zig");
const server = &@import("../main.zig").server;
pub fn coerceNumber(comptime x: type, number: zlua.Number) error{InvalidNumber}!x {
if (number < std.math.minInt(x) or number > std.math.maxInt(x) or std.math.isNan(number)) {
return error.InvalidNumber;
@ -43,3 +47,14 @@ pub fn toStringEx(L: *zlua.Lua) [:0]const u8 {
L.protectedCall(.{ .args = 1, .results = 1 }) catch return errstr;
return L.toString(-1) catch errstr;
}
pub fn viewById(view_id: u64) ?*View {
if (view_id == 0) {
if(server.seat.focused_surface) |fs| {
if(fs == .view) return fs.view;
}
} else {
return server.root.viewById(view_id);
}
return null;
}

View file

@ -68,10 +68,12 @@ pub fn check(L: *zlua.Lua) i32 {
// ---Get the id for the focused view
// ---@return view_id?
pub fn get_focused_id(L: *zlua.Lua) i32 {
if(server.seat.focused_view) |view| {
L.pushInteger(@intCast(view.id));
if(server.seat.focused_surface) |fs| {
if(fs == .view) {
L.pushInteger(@intCast(fs.view.id));
return 1;
}
}
L.pushNil();
return 1;
@ -82,8 +84,7 @@ pub fn get_focused_id(L: *zlua.Lua) i32 {
pub fn close(L: *zlua.Lua) i32 {
const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L);
const view: ?*View = if (view_id == 0) server.seat.focused_view else server.root.viewById(view_id);
if(view) |v| {
if(LuaUtils.viewById(view_id)) |v| {
v.close();
}
@ -100,8 +101,7 @@ pub fn set_position(L: *zlua.Lua) i32 {
const x = LuaUtils.coerceNumber(i32, L.checkNumber(2)) catch L.raiseErrorStr("The x must be > -inf and < inf", .{});
const y = LuaUtils.coerceNumber(i32, L.checkNumber(3)) catch L.raiseErrorStr("The y must be > -inf and < inf", .{});
const view: ?*View = if (view_id == 0) server.seat.focused_view else server.root.viewById(view_id);
if(view) |v| {
if(LuaUtils.viewById(view_id)) |v| {
v.setPosition(x, y);
}
@ -114,8 +114,7 @@ pub fn set_position(L: *zlua.Lua) i32 {
// ---@return { x: integer, y: integer }? Position of the view
pub fn get_position(L: *zlua.Lua) i32 {
const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L);
const view: ?*View = if (view_id == 0) server.seat.focused_view else server.root.viewById(view_id);
if (view) |v| {
if (LuaUtils.viewById(view_id)) |v| {
L.newTable();
_ = L.pushString("x");
@ -146,8 +145,7 @@ pub fn set_size(L: *zlua.Lua) i32 {
const width = LuaUtils.coerceNumber(u32, L.checkNumber(2)) catch L.raiseErrorStr("The width must be >= 0 and < inf", .{});
const height = LuaUtils.coerceNumber(u32, L.checkNumber(3)) catch L.raiseErrorStr("The height must be >= 0 and < inf", .{});
const view: ?*View = if (view_id == 0) server.seat.focused_view else server.root.viewById(view_id);
if(view) |v| {
if(LuaUtils.viewById(view_id)) |v| {
v.setSize(@intCast(width), @intCast(height));
}
@ -160,8 +158,7 @@ pub fn set_size(L: *zlua.Lua) i32 {
// ---@return { width: integer, height: integer }? Size of the view
pub fn get_size(L: *zlua.Lua) i32 {
const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L);
const view: ?*View = if (view_id == 0) server.seat.focused_view else server.root.viewById(view_id);
if (view) |v| {
if (LuaUtils.viewById(view_id)) |v| {
L.newTable();
_ = L.pushString("width");
@ -185,23 +182,9 @@ pub fn set_focused(L: *zlua.Lua) i32 {
const view_id: ?c_longlong = L.optInteger(1);
if(view_id == null) {
if(server.seat.focused_view != null) {
server.seat.focused_view.?.focused = false;
server.seat.focused_view = null;
}
L.pushNil();
return 1;
}
if (view_id == null) {
L.pushNil();
return 1;
}
if(server.root.viewById(@intCast(view_id.?))) |view| {
view.setFocused();
L.pushNil();
return 1;
server.seat.focusSurface(null);
} else if(server.root.viewById(@intCast(view_id.?))) |view| {
server.seat.focusSurface(.{ .view = view });
}
L.pushNil();
@ -213,14 +196,8 @@ pub fn set_focused(L: *zlua.Lua) i32 {
pub fn toggle_fullscreen(L: *zlua.Lua) i32 {
const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L);
const view: ?*View = if (view_id == 0) server.seat.focused_view else server.root.viewById(view_id);
if(view) |v| {
std.log.debug("toggling fullscreen", .{});
if(v.fullscreen) {
v.toContent();
} else {
v.toFullscreen();
}
if(LuaUtils.viewById(view_id)) |v| {
v.toggleFullscreen();
}
L.pushNil();
@ -233,8 +210,7 @@ pub fn toggle_fullscreen(L: *zlua.Lua) i32 {
pub fn get_title(L: *zlua.Lua) i32 {
const view_id: u64 = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L);
const view: ?*View = if (view_id == 0) server.seat.focused_view else server.root.viewById(view_id);
if(view) |v| {
if(LuaUtils.viewById(view_id)) |v| {
if(v.xdg_toplevel.title == null) {
L.pushNil();
return 1;
@ -254,8 +230,7 @@ pub fn get_title(L: *zlua.Lua) i32 {
pub fn get_app_id(L: *zlua.Lua) i32 {
const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L);
const view: ?*View = if (view_id == 0) server.seat.focused_view else server.root.viewById(view_id);
if(view) |v| {
if(LuaUtils.viewById(view_id)) |v| {
if(v.xdg_toplevel.app_id == null) {
L.pushNil();
return 1;