From a90c106b8c6dcb786e9868e41e1311a1856eb079 Mon Sep 17 00:00:00 2001 From: Harrison DiAmbrosio Date: Mon, 15 Dec 2025 20:00:49 -0500 Subject: [PATCH] focusing of surfaces or more streamlined --- runtime/share/mezzaluna/master.lua | 4 -- src/Cursor.zig | 40 ++++++++------- src/Output.zig | 19 ++++++- src/Seat.zig | 82 +++++++++++++++++++++++++++++- src/Server.zig | 2 +- src/View.zig | 57 +++++++-------------- src/lua/LuaUtils.zig | 15 ++++++ src/lua/View.zig | 59 +++++++-------------- 8 files changed, 171 insertions(+), 107 deletions(-) diff --git a/runtime/share/mezzaluna/master.lua b/runtime/share/mezzaluna/master.lua index afb361d..68b6fcf 100644 --- a/runtime/share/mezzaluna/master.lua +++ b/runtime/share/mezzaluna/master.lua @@ -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 diff --git a/src/Cursor.zig b/src/Cursor.zig index d193771..d0180f8 100644 --- a/src/Cursor.zig +++ b/src/Cursor.zig @@ -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)) - ); - } + _ = 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")) { - cursor.mode = .resize; - _ = view.xdg_toplevel.setResizing(true); + if(fs == .view) { + cursor.mode = .resize; + _ = fs.view.xdg_toplevel.setResizing(true); + } } } } diff --git a/src/Output.zig b/src/Output.zig index a9dca9b..e032784 100644 --- a/src/Output.zig +++ b/src/Output.zig @@ -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), diff --git a/src/Seat.zig b/src/Seat.zig index 5617a26..8b2bafc 100644 --- a/src/Seat.zig +++ b/src/Seat.zig @@ -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; diff --git a/src/Server.zig b/src/Server.zig index a943678..5d81622 100644 --- a/src/Server.zig +++ b/src/Server.zig @@ -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", .{}); } diff --git a/src/View.zig b/src/View.zig index e96b698..08daa3e 100644 --- a/src/View.zig +++ b/src/View.zig @@ -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| { - self.scene_tree.node.reparent(output.layers.fullscreen); - } - self.fullscreen = true; - _ = self.xdg_toplevel.setFullscreen(true); -} + 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(); + } -pub fn toContent(self: *View) void { - if(server.seat.focused_output) |output| { - self.scene_tree.node.reparent(output.layers.content); + self.scene_tree.node.reparent(output.layers.fullscreen); + 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 { diff --git a/src/lua/LuaUtils.zig b/src/lua/LuaUtils.zig index bcc5761..c924ec6 100644 --- a/src/lua/LuaUtils.zig +++ b/src/lua/LuaUtils.zig @@ -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; +} diff --git a/src/lua/View.zig b/src/lua/View.zig index e91110d..7218feb 100644 --- a/src/lua/View.zig +++ b/src/lua/View.zig @@ -68,9 +68,11 @@ 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)); - return 1; + if(server.seat.focused_surface) |fs| { + if(fs == .view) { + L.pushInteger(@intCast(fs.view.id)); + return 1; + } } L.pushNil(); @@ -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;