fullscreen

This commit is contained in:
Harrison DiAmbrosio 2025-12-14 20:35:07 -05:00
commit 49e0875a85
12 changed files with 488 additions and 150 deletions

View file

@ -1,4 +1,5 @@
local env_conf = os.getenv("XDG_CONFIG_HOME")
if not env_conf then
env_conf = os.getenv("HOME")
if not env_conf then
@ -11,7 +12,7 @@ end
package.path = package.path..";"..mez.fs.joinpath(mez.path.runtime, "?.lua")
mez.inspect = require("inspect").inspect
mez.path.base_config = mez.fs.joinpath(mez.path.runtime, "base_config.lua")
mez.path.base_config = mez.fs.joinpath(mez.path.runtime, "master.lua")
if not mez.path.config then
mez.path.config = mez.fs.joinpath(env_conf, "mez", "init.lua")

View file

@ -1,3 +1,11 @@
mez.input.add_keymap("alt", "g", {
press = function ()
for _, id in ipairs(mez.view.get_all_ids()) do
print(id)
end
end
})
local master = function()
local config = {
tag_count = 5,
@ -6,7 +14,7 @@ local master = function()
local ctx = {
master_ratio = 0.5,
tags = {},
tag_id = 1
tag_id = 1,
}
local tile_onscreen = function(tag_id, res)
@ -124,9 +132,10 @@ local master = function()
end
})
mez.hook.add("ViewPointerMotion", {
callback = function (view_id, cursor_x, cursor_y)
mez.view.set_focused(view_id)
mez.hook.add("ViewRequestFullscreen", {
callback = function (view_id)
mez.view.toggle_fullscreen(view_id)
end
})
@ -248,6 +257,29 @@ local master = function()
end
})
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
mez.input.add_keymap("alt|shift", "F", {
press = function() fullscreen(0) end
})
mez.hook.add("ViewRequestFullscreen", {
callback = fullscreen
})
mez.hook.add("ViewPointerMotion", {
callback = function (view_id, cursor_x, cursor_y)
mez.view.set_focused(view_id)
end
})
for i = 1, 12 do
mez.input.add_keymap("ctrl|alt", "XF86Switch_VT_"..i, {
press = function() mez.api.change_vt(i) end
@ -282,4 +314,3 @@ function print_table(tbl, indent, seen)
end
end
end

View file

@ -32,7 +32,7 @@ mode: enum { passthrough, move, resize } = .passthrough,
drag: struct {
start_x: c_int,
start_y: c_int,
view: ?*View,
view: ?*View,
view_offset_x: ?c_int,
view_offset_y: ?c_int,
},
@ -86,8 +86,8 @@ pub fn processCursorMotion(self: *Cursor, time_msec: u32) void {
// Exit the switch if no focused output exists
if (output == null) return;
const viewAtResult = output.?.viewAt(self.wlr_cursor.x, self.wlr_cursor.y);
if (viewAtResult == null) {
const surfaceAtResult = output.?.surfaceAt(self.wlr_cursor.x, self.wlr_cursor.y);
if (surfaceAtResult == null) {
self.wlr_cursor.setXcursor(self.x_cursor_manager, "default");
server.seat.wlr_seat.pointerClearFocus();
@ -98,10 +98,16 @@ pub fn processCursorMotion(self: *Cursor, time_msec: u32) void {
return;
}
server.events.exec("ViewPointerMotion", .{viewAtResult.?.view.id, self.wlr_cursor.x, self.wlr_cursor.y});
switch (surfaceAtResult.?.scene_node_data.*) {
.view => {
server.events.exec("ViewPointerMotion", .{surfaceAtResult.?.scene_node_data.view.id, self.wlr_cursor.x, self.wlr_cursor.y});
},
.layer_surface => { },
else => unreachable
}
server.seat.wlr_seat.pointerNotifyEnter(viewAtResult.?.surface, viewAtResult.?.sx, viewAtResult.?.sy);
server.seat.wlr_seat.pointerNotifyMotion(time_msec, viewAtResult.?.sx, viewAtResult.?.sy);
server.seat.wlr_seat.pointerNotifyEnter(surfaceAtResult.?.surface, surfaceAtResult.?.sx, surfaceAtResult.?.sy);
server.seat.wlr_seat.pointerNotifyMotion(time_msec, surfaceAtResult.?.sx, surfaceAtResult.?.sy);
},
.move => { // TODO: Have these behave more like pointer motion
if(self.drag.view) |view| {

127
src/Debug.zig Normal file
View file

@ -0,0 +1,127 @@
const Debug = @This();
const std = @import("std");
const wlr = @import("wlroots");
const server = &@import("main.zig").server;
const gpa = std.heap.c_allocator;
const Utils = @import("Utils.zig");
const SceneNodeData = @import("SceneNodeData.zig").SceneNodeData;
pub fn debugPrintSceneTree() void {
std.log.debug("=== SCENE TREE DEBUG ===", .{});
printNode(&server.root.scene.tree.node, 0);
std.log.debug("=== END SCENE TREE ===", .{});
}
fn printNode(node: *wlr.SceneNode, depth: usize) void {
errdefer Utils.oomPanic();
var buffer: std.ArrayList(u8) = try .initCapacity(gpa, 512);
defer buffer.deinit(gpa);
const writer = buffer.writer(gpa);
// Add indentation
for (0..depth) |_| {
writer.writeAll(" ") catch unreachable;
}
// Print node type and position
const type_name = switch (node.type) {
.tree => "TREE",
.rect => "RECT",
.buffer => "BUFFER"
};
writer.print("{s} @ ({d}, {d}) enabled={}", .{
type_name,
node.x,
node.y,
node.enabled
}) catch unreachable;
// Add associated data if present
if (node.data) |data| {
const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(data));
switch (scene_node_data.*) {
.root => {
writer.print(" → Root Scene Tree", .{}) catch unreachable;
},
.output => |output| {
writer.print(" → Output: {s} (focused={}, id={})", .{
output.wlr_output.name,
output.focused,
output.id,
}) catch unreachable;
},
.output_layer => {
writer.print(" → Output Layer", .{}) catch unreachable;
},
.view => |view| {
writer.print(" → View: id={} mapped={} focused={}", .{
view.id,
view.xdg_toplevel.base.surface.mapped,
view.focused,
}) catch unreachable;
if (view.xdg_toplevel.title) |title| {
writer.print(" title=\"{s}\"", .{title}) catch unreachable;
}
},
.layer_surface => |layer| {
const layer_name = switch (layer.wlr_layer_surface.current.layer) {
.background => "background",
.bottom => "bottom",
.top => "top",
.overlay => "overlay",
else => "unknown",
};
writer.print(" → LayerSurface: layer={s} mapped={}", .{
layer_name,
layer.wlr_layer_surface.surface.mapped,
}) catch unreachable;
const namespace = std.mem.span(layer.wlr_layer_surface.namespace);
if (namespace.len > 0) {
writer.print(" namespace=\"{s}\"", .{namespace}) catch unreachable;
}
}
}
}
// Add buffer-specific info
if (node.type == .buffer) {
const scene_buffer = wlr.SceneBuffer.fromNode(node);
writer.print(" buffer: {d}x{d}", .{
if (scene_buffer.buffer == null) -1 else scene_buffer.buffer.?.width,
if (scene_buffer.buffer == null) -1 else scene_buffer.buffer.?.height,
}) catch unreachable;
// Check if it's a surface
if (wlr.SceneSurface.tryFromBuffer(scene_buffer)) |scene_surface| {
writer.print(" → Surface: {*}", .{scene_surface.surface}) catch unreachable;
}
}
// Print the complete line
std.log.debug("{s}", .{buffer.items});
// Recursively print children if this is a tree
if (node.type == .tree) {
const tree = wlr.SceneTree.fromNode(node);
var it = tree.children.iterator(.forward);
var child_count: usize = 0;
while (it.next()) |child| {
child_count += 1;
printNode(child, depth + 1);
}
if (child_count == 0) {
var empty_buffer: std.ArrayList(u8) = try .initCapacity(gpa, 512);
defer empty_buffer.deinit(gpa);
for (0..depth) |_| {
empty_buffer.writer(gpa).writeAll("\t") catch unreachable;
}
empty_buffer.writer(gpa).writeAll(" (no children)") catch unreachable;
std.log.debug("{s}", .{empty_buffer.items});
}
}
}

View file

@ -6,12 +6,13 @@ const wlr = @import("wlroots");
const Utils = @import("Utils.zig");
const Output = @import("Output.zig");
const SceneNodeData = @import("SceneNodeData.zig");
const SceneNodeData = @import("SceneNodeData.zig").SceneNodeData;
const gpa = std.heap.c_allocator;
const server = &@import("main.zig").server;
output: *Output,
scene_node_data: SceneNodeData,
wlr_layer_surface: *wlr.LayerSurfaceV1,
scene_layer_surface: *wlr.SceneLayerSurfaceV1,
@ -27,9 +28,22 @@ pub fn init(wlr_layer_surface: *wlr.LayerSurfaceV1) *LayerSurface {
const self = try gpa.create(LayerSurface);
self.* = .{
.output = @ptrCast(@alignCast(wlr_layer_surface.output.?.data)),
.output = blk: {
// These block things are dangerous
// There was no need for this
// But I cannot be stopped
// - Powerhungry programmer
const data = wlr_layer_surface.output.?.data;
if(data == null) unreachable;
const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(wlr_layer_surface.output.?.data.?));
break :blk switch(scene_node_data.*) {
.output => @fieldParentPtr("scene_node_data", scene_node_data),
else => unreachable
};
},
.wlr_layer_surface = wlr_layer_surface,
.scene_layer_surface = undefined,
.scene_node_data = .{ .layer_surface = self }
};
if(server.seat.focused_output) |output| {
@ -45,11 +59,8 @@ pub fn init(wlr_layer_surface: *wlr.LayerSurfaceV1) *LayerSurface {
};
}
try SceneNodeData.setData(
&self.scene_layer_surface.tree.node,
.{ .layer_surface = self },
);
self.wlr_layer_surface.surface.data = &self.scene_layer_surface.tree.node;
self.wlr_layer_surface.surface.data = &self.scene_node_data;
self.scene_layer_surface.tree.node.data = &self.scene_node_data;
self.wlr_layer_surface.events.destroy.add(&self.destroy);
self.wlr_layer_surface.surface.events.map.add(&self.map);
@ -93,8 +104,10 @@ fn handleDestroy(
fn handleMap(
listener: *wl.Listener(void)
) void {
const layer: *LayerSurface = @fieldParentPtr("map", listener);
layer.allowKeyboard();
const layer_suraface: *LayerSurface = @fieldParentPtr("map", listener);
std.log.debug("layer surface mapped", .{});
layer_suraface.output.arrangeLayers();
layer_suraface.allowKeyboard();
}
fn handleUnmap(listener: *wl.Listener(void)) void {
@ -102,6 +115,8 @@ fn handleUnmap(listener: *wl.Listener(void)) void {
// FIXME: this crashes mez when killing mez
layer_surface.output.arrangeLayers();
// TODO: Idk if this should be deiniting the layer surface entirely
layer_surface.deinit();
}

View file

@ -5,11 +5,13 @@ const zwlr = @import("wayland").server.zwlr;
const wlr = @import("wlroots");
const std = @import("std");
const Server = @import("Server.zig");
const Utils = @import("Utils.zig");
const Server = @import("Server.zig");
const View = @import("View.zig");
const LayerSurface = @import("LayerSurface.zig");
const SceneNodeData = @import("SceneNodeData.zig");
const SceneNodeData = @import("SceneNodeData.zig").SceneNodeData;
const posix = std.posix;
const gpa = std.heap.c_allocator;
@ -21,6 +23,7 @@ id: u64,
wlr_output: *wlr.Output,
state: wlr.Output.State,
tree: *wlr.SceneTree,
scene_node_data: SceneNodeData,
scene_output: *wlr.SceneOutput,
layers: struct {
@ -32,6 +35,15 @@ layers: struct {
overlay: *wlr.SceneTree
},
layer_scene_node_data: struct {
background: SceneNodeData,
bottom: SceneNodeData,
content: SceneNodeData,
top: SceneNodeData,
fullscreen: SceneNodeData,
overlay: SceneNodeData
},
frame: wl.Listener(*wlr.Output) = .init(handleFrame),
request_state: wl.Listener(*wlr.Output.event.RequestState) = .init(handleRequestState),
destroy: wl.Listener(*wlr.Output) = .init(handleDestroy),
@ -45,6 +57,7 @@ pub fn init(wlr_output: *wlr.Output) ?*Output {
const self = try gpa.create(Output);
self.* = .{
.focused = false,
.id = @intFromPtr(wlr_output),
@ -58,10 +71,21 @@ pub fn init(wlr_output: *wlr.Output) ?*Output {
.fullscreen = try self.tree.createSceneTree(),
.overlay = try self.tree.createSceneTree(),
},
.layer_scene_node_data = .{
.background = .{ .output_layer = self.layers.background },
.bottom = .{ .output_layer = self.layers.bottom },
.content = .{ .output_layer = self.layers.content },
.top = .{ .output_layer = self.layers.top },
.fullscreen = .{ .output_layer = self.layers.fullscreen },
.overlay = .{ .output_layer = self.layers.overlay }
},
.scene_output = try server.root.scene.createSceneOutput(wlr_output),
.scene_node_data = SceneNodeData{ .output = self },
.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);
@ -88,7 +112,15 @@ pub fn init(wlr_output: *wlr.Output) ?*Output {
server.root.scene_output_layout.addOutput(layout_output, self.scene_output);
self.setFocused();
wlr_output.data = self;
self.wlr_output.data = &self.scene_node_data;
self.tree.node.data = &self.scene_node_data;
self.layers.background.node.data = &self.layer_scene_node_data.background;
self.layers.bottom.node.data = &self.layer_scene_node_data.bottom;
self.layers.content.node.data = &self.layer_scene_node_data.content;
self.layers.top.node.data = &self.layer_scene_node_data.top;
self.layers.fullscreen.node.data = &self.layer_scene_node_data.fullscreen;
self.layers.overlay.node.data = &self.layer_scene_node_data.overlay;
server.events.exec("OutputInitPost", .{self.id});
@ -130,7 +162,6 @@ pub fn configureLayers(self: *Output) void {
self.wlr_output.effectiveResolution(&output_box.width, &output_box.height);
// Should calculate usable area here for LUA view positioning
for ([_]zwlr.LayerShellV1.Layer{ .background, .bottom, .top, .overlay }) |layer| {
const tree = blk: {
const trees = [_]*wlr.SceneTree{
@ -146,43 +177,85 @@ pub fn configureLayers(self: *Output) void {
while(it.next()) |node| {
if(node.data == null) continue;
const layer_surface: *wlr.LayerSurfaceV1 = @ptrCast(@alignCast(node.data.?));
_ = layer_surface.configure(@intCast(output_box.width), @intCast(output_box.height));
const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(node.data.?));
switch (scene_node_data.*) {
.layer_surface => {
_ = scene_node_data.layer_surface.wlr_layer_surface.configured(
@intCast(output_box.width),
@intCast(output_box.height)
);
},
else => {
std.log.err("Something other than a layer surface found in layer surface scene trees", .{});
unreachable;
}
}
}
}
}
const ViewAtResult = struct {
view: *View,
const SurfaceAtResult = struct {
scene_node_data: *SceneNodeData,
surface: *wlr.Surface,
sx: f64,
sy: f64,
};
pub fn viewAt(self: *Output, lx: f64, ly: f64) ?ViewAtResult {
pub fn surfaceAt(self: *Output, lx: f64, ly: f64) ?SurfaceAtResult {
var sx: f64 = undefined;
var sy: f64 = undefined;
if(self.layers.content.node.at(lx, ly, &sx, &sy)) |node| {
if (node.type != .buffer) return null;
const scene_buffer = wlr.SceneBuffer.fromNode(node);
const scene_surface = wlr.SceneSurface.tryFromBuffer(scene_buffer) orelse return null;
const layers = [_]*wlr.SceneTree{
self.layers.top,
self.layers.overlay,
self.layers.fullscreen,
self.layers.content,
self.layers.bottom,
self.layers.background
};
var it: ?*wlr.SceneTree = node.parent;
for(layers) |layer| {
const node = layer.node.at(lx, ly, &sx, &sy);
if(node == null) continue;
while (it) |n| : (it = n.node.parent) {
if (n.node.data == null) continue;
const surface: ?*wlr.Surface = blk: {
if (node.?.type == .buffer) {
const scene_buffer = wlr.SceneBuffer.fromNode(node.?);
if (wlr.SceneSurface.tryFromBuffer(scene_buffer)) |scene_surface| {
break :blk scene_surface.surface;
}
}
break :blk null;
};
if(surface == null) continue;
const view: *View = @ptrCast(@alignCast(n.node.data.?));
const scene_node_data: *SceneNodeData = blk: {
var n = node.?;
while (true) {
if (@as(?*SceneNodeData, @ptrCast(@alignCast(n.data)))) |snd| {
break :blk snd;
}
if (n.parent) |parent_tree| {
n = &parent_tree.node;
} else {
continue;
}
}
};
return ViewAtResult{
.view = view,
.surface = scene_surface.surface,
.sx = sx,
.sy = sy,
};
switch (scene_node_data.*) {
.layer_surface, .view => {
return SurfaceAtResult{
.scene_node_data = scene_node_data,
.surface = surface.?,
.sx = sx,
.sy = sy,
};
},
else => continue
}
}
return null;
}
@ -246,9 +319,17 @@ pub fn arrangeLayers(self: *Output) void {
inline for (@typeInfo(zwlr.LayerShellV1.Layer).@"enum".fields) |comptime_layer| {
const layer: *wlr.SceneTree = @field(self.layers, comptime_layer.name);
var it = layer.children.safeIterator(.forward);
while (it.next()) |node| {
if (@as(?*SceneNodeData, @alignCast(@ptrCast(node.data)))) |node_data| {
const layer_surface = node_data.data.layer_surface;
if(node.data == null) continue;
// if (@as(?*SceneNodeData, @alignCast(@ptrCast(node.data)))) |node_data| {
const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(node.data.?));
const layer_surface: *LayerSurface = switch(scene_node_data.*) {
.layer_surface => @fieldParentPtr("scene_node_data", scene_node_data),
else => continue
};
if (!layer_surface.wlr_layer_surface.initialized) continue;
@ -263,7 +344,6 @@ pub fn arrangeLayers(self: *Output) void {
// const x = layer_surface.scene_layer_surface.tree.node.x;
// const y = layer_surface.scene_layer_surface.tree.node.y;
// layer_surface.scene_layer_surface.tree.node.setPosition(x, y);
}
}
}
}

View file

@ -1,22 +1,26 @@
/// The root of Mezzaluna is, you guessed it, the root of many of the systems mez needs:
/// - Managing outputs
/// -
const Root = @This();
const std = @import("std");
const wl = @import("wayland").server.wl;
const wlr = @import("wlroots");
const Output = @import("Output.zig");
const View = @import("View.zig");
const Utils = @import("Utils.zig");
const server = &@import("main.zig").server;
const gpa = std.heap.c_allocator;
const Output = @import("Output.zig");
const View = @import("View.zig");
const LayerSurface = @import("LayerSurface.zig");
const SceneNodeData = @import("SceneNodeData.zig").SceneNodeData;
const Utils = @import("Utils.zig");
xdg_toplevel_decoration_manager: *wlr.XdgDecorationManagerV1,
scene: *wlr.Scene,
scene_node_data: SceneNodeData,
waiting_room: *wlr.SceneTree,
scene_output_layout: *wlr.SceneOutputLayout,
@ -35,11 +39,14 @@ pub fn init(self: *Root) void {
self.* = .{
.scene = scene,
.scene_node_data = .{ .root = self },
.waiting_room = try scene.tree.createSceneTree(),
.output_layout = output_layout,
.xdg_toplevel_decoration_manager = try wlr.XdgDecorationManagerV1.create(server.wl_server),
.scene_output_layout = try scene.attachOutputLayout(output_layout),
};
self.scene.tree.node.data = &self.scene_node_data;
}
pub fn deinit(self: *Root) void {
@ -48,8 +55,16 @@ pub fn deinit(self: *Root) void {
while(it.next()) |node| {
if(node.data == null) continue;
const view: *View = @ptrCast(@alignCast(node.data.?));
view.deinit();
const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(node.data.?));
switch(scene_node_data.*) {
.output => {
scene_node_data.output.deinit();
},
else => {
std.log.debug("The root has a child that is not an output", .{});
unreachable;
}
}
}
self.output_layout.destroy();
@ -62,14 +77,29 @@ pub fn viewById(self: *Root, id: u64) ?*View {
while(output_it.next()) |o| {
if(o.output.data == null) continue;
const output: *Output = @ptrCast(@alignCast(o.output.data.?));
const output_snd: *SceneNodeData = @ptrCast(@alignCast(o.output.data.?));
const output: *Output = switch (output_snd.*) {
.output => |output_ptr| output_ptr,
else => {
std.log.err("Incorrect scene node type found", .{});
unreachable;
}
};
var node_it = output.layers.content.children.iterator(.forward);
while(node_it.next()) |node| {
if(node.data == null) continue;
const view: *View = @as(*View, @ptrCast(@alignCast(node.data.?)));
if(view.id == id) return view;
const view_snd: *SceneNodeData = @ptrCast(@alignCast(node.data.?));
// TODO: Should we assert that we want only views to be here
// -- Basically should we use switch statements for snd interactions
// -- Or if statements, for simplicity
if(view_snd.* == .view and view_snd.view.id == id) {
return view_snd.view;
}
}
}

View file

@ -1,54 +1,22 @@
const SceneNodeData = @This();
const std = @import("std");
const wlr = @import("wlroots");
const wl = @import("wayland").server.wl;
const View = @import("View.zig");
const LayerSurface = @import("LayerSurface.zig");
const Output = @import("Output.zig");
const Root = @import("Root.zig");
const gpa = std.heap.c_allocator;
pub const Data = union(enum) {
view: *View,
layer_surface: *LayerSurface,
const SceneNodeType = enum {
view,
layer_surface,
output,
output_layer,
root
};
node: *wlr.SceneNode,
data: Data,
destroy: wl.Listener(void) = wl.Listener(void).init(handleDestroy),
pub fn setData(node: *wlr.SceneNode, data: Data) !void {
const scene_node_data = try gpa.create(SceneNodeData);
scene_node_data.* = .{
.node = node,
.data = data,
};
node.data = scene_node_data;
node.events.destroy.add(&scene_node_data.destroy);
}
fn handleDestroy(listener: *wl.Listener(void)) void {
const scene_node_data: *SceneNodeData = @fieldParentPtr("destroy", listener);
scene_node_data.destroy.link.remove();
scene_node_data.node.data = null;
gpa.destroy(scene_node_data);
}
pub fn getFromNode(node: *wlr.SceneNode) ?*SceneNodeData {
var n = node;
while (true) {
if (@as(?*SceneNodeData, @alignCast(@ptrCast(n.data)))) |scene_node_data| {
return scene_node_data;
}
if (n.parent) |parent_tree| {
n = &parent_tree.node;
} else {
return null;
}
}
}
pub const SceneNodeData = union(SceneNodeType) {
view: *View,
layer_surface: *LayerSurface,
output: *Output,
output_layer: *wlr.SceneTree,
root: *Root
};

View file

@ -4,20 +4,21 @@ const std = @import("std");
const wl = @import("wayland").server.wl;
const wlr = @import("wlroots");
const Root = @import("Root.zig");
const Seat = @import("Seat.zig");
const Cursor = @import("Cursor.zig");
const Keyboard = @import("Keyboard.zig");
const LayerSurface = @import("LayerSurface.zig");
const Output = @import("Output.zig");
const View = @import("View.zig");
const Utils = @import("Utils.zig");
const Keymap = @import("types/Keymap.zig");
const Hook = @import("types/Hook.zig");
const Events = @import("types/Events.zig");
const Popup = @import("Popup.zig");
const RemoteLua = @import("RemoteLua.zig");
const Root = @import("Root.zig");
const Seat = @import("Seat.zig");
const Cursor = @import("Cursor.zig");
const Keyboard = @import("Keyboard.zig");
const LayerSurface = @import("LayerSurface.zig");
const Output = @import("Output.zig");
const View = @import("View.zig");
const Keymap = @import("types/Keymap.zig");
const Hook = @import("types/Hook.zig");
const Events = @import("types/Events.zig");
const Popup = @import("Popup.zig");
const RemoteLua = @import("RemoteLua.zig");
const RemoteLuaManager = @import("RemoteLuaManager.zig");
const Utils = @import("Utils.zig");
const SceneNodeData = @import("SceneNodeData.zig").SceneNodeData;
const gpa = std.heap.c_allocator;
const server = &@import("main.zig").server;
@ -34,8 +35,7 @@ shm: *wlr.Shm,
xdg_shell: *wlr.XdgShell,
layer_shell: *wlr.LayerShellV1,
xdg_toplevel_decoration_manager: *wlr.XdgDecorationManagerV1,
// Input
xdg_activation: *wlr.XdgActivationV1,
allocator: *wlr.Allocator,
@ -43,7 +43,7 @@ root: Root,
seat: Seat,
cursor: Cursor,
// lua data
// Lua data
keymaps: std.AutoHashMap(u64, Keymap),
hooks: std.ArrayList(*Hook),
events: Events,
@ -53,14 +53,11 @@ remote_lua_clients: std.DoublyLinkedList,
new_input: wl.Listener(*wlr.InputDevice) = .init(handleNewInput),
new_output: wl.Listener(*wlr.Output) = .init(handleNewOutput),
// backend.events.destroy
// XdgShell listeners
new_xdg_toplevel: wl.Listener(*wlr.XdgToplevel) = .init(handleNewXdgToplevel),
new_xdg_popup: wl.Listener(*wlr.XdgPopup) = .init(handleNewXdgPopup),
new_xdg_toplevel_decoration: wl.Listener(*wlr.XdgToplevelDecorationV1) = .init(handleNewXdgToplevelDecoration),
// LayerShell Listeners
new_layer_surface: wl.Listener(*wlr.LayerSurfaceV1) = .init(handleNewLayerSurface),
request_activate: wl.Listener(*wlr.XdgActivationV1.event.RequestActivate) = .init(handleRequestActivate),
pub fn init(self: *Server) void {
errdefer Utils.oomPanic();
@ -94,6 +91,7 @@ pub fn init(self: *Server) void {
.xdg_shell = try wlr.XdgShell.create(wl_server, 2),
.layer_shell = try wlr.LayerShellV1.create(wl_server, 4),
.xdg_toplevel_decoration_manager = try wlr.XdgDecorationManagerV1.create(self.wl_server),
.xdg_activation = try wlr.XdgActivationV1.create(self.wl_server),
.event_loop = event_loop,
.session = session,
.compositor = try wlr.Compositor.create(wl_server, 6, renderer),
@ -133,19 +131,13 @@ pub fn init(self: *Server) void {
self.root.scene.setGammaControlManagerV1(try wlr.GammaControlManagerV1.create(self.wl_server));
// Add event listeners to events
// Backedn events
self.backend.events.new_input.add(&self.new_input);
self.backend.events.new_output.add(&self.new_output);
// XdgShell events
self.xdg_shell.events.new_toplevel.add(&self.new_xdg_toplevel);
self.xdg_shell.events.new_popup.add(&self.new_xdg_popup);
// XdgDecorationManagerV1 events
self.xdg_toplevel_decoration_manager.events.new_toplevel_decoration.add(&self.new_xdg_toplevel_decoration);
// LayerShell events
self.layer_shell.events.new_surface.add(&self.new_layer_surface);
self.xdg_activation.events.request_activate.add(&self.request_activate);
self.events.exec("ServerStartPost", .{});
}
@ -217,14 +209,15 @@ fn handleNewXdgToplevelDecoration(
}
}
fn handleNewXdgPopup(_: *wl.Listener(*wlr.XdgPopup), xdg_popup: *wlr.XdgPopup) void {
_ = xdg_popup;
fn handleNewXdgPopup(_: *wl.Listener(*wlr.XdgPopup), _: *wlr.XdgPopup) void {
std.log.debug("Unimplemented Server.handleNewXdgPopup\n", .{});
}
fn handleNewLayerSurface(
_: *wl.Listener(*wlr.LayerSurfaceV1),
layer_surface: *wlr.LayerSurfaceV1
) void {
std.log.debug("requested layer shell\n", .{});
if (layer_surface.output == null) {
if (server.seat.focused_output == null) {
std.log.err("No output available for new layer surface", .{});
@ -237,3 +230,17 @@ fn handleNewLayerSurface(
_ = LayerSurface.init(layer_surface);
}
fn handleRequestActivate(
_: *wl.Listener(*wlr.XdgActivationV1.event.RequestActivate),
event: *wlr.XdgActivationV1.event.RequestActivate,
) void {
if(event.surface.data == null) return;
const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(event.surface.data.?));
if(scene_node_data.* == .view) {
scene_node_data.view.setFocused();
} else {
std.log.warn("Ignoring request to activate non-view", .{});
}
}

View file

@ -6,6 +6,8 @@ const wlr = @import("wlroots");
const Popup = @import("Popup.zig");
const Output = @import("Output.zig");
const SceneNodeData = @import("SceneNodeData.zig").SceneNodeData;
const Utils = @import("Utils.zig");
const gpa = std.heap.c_allocator;
@ -13,6 +15,7 @@ const server = &@import("main.zig").server;
mapped: bool,
focused: bool,
fullscreen: bool,
id: u64,
// workspace: Workspace,
@ -20,6 +23,7 @@ output: ?*Output,
xdg_toplevel: *wlr.XdgToplevel,
xdg_toplevel_decoration: ?*wlr.XdgToplevelDecorationV1,
scene_tree: *wlr.SceneTree,
scene_node_data: SceneNodeData,
// Surface Listeners
map: wl.Listener(void) = .init(handleMap),
@ -56,12 +60,15 @@ pub fn initFromTopLevel(xdg_toplevel: *wlr.XdgToplevel) *View {
self.* = .{
.focused = false,
.mapped = false,
.fullscreen = false,
.id = @intFromPtr(xdg_toplevel),
.output = null,
.xdg_toplevel = xdg_toplevel,
.scene_tree = undefined,
.xdg_toplevel_decoration = null,
.scene_node_data = .{ .view = self }
};
self.xdg_toplevel.base.surface.events.unmap.add(&self.unmap);
@ -76,8 +83,8 @@ pub fn initFromTopLevel(xdg_toplevel: *wlr.XdgToplevel) *View {
self.scene_tree = try server.root.waiting_room.createSceneXdgSurface(xdg_toplevel.base);
}
self.scene_tree.node.data = self;
self.xdg_toplevel.base.data = self.scene_tree;
self.scene_tree.node.data = &self.scene_node_data;
self.xdg_toplevel.base.data = &self.scene_node_data;
self.xdg_toplevel.events.destroy.add(&self.destroy);
self.xdg_toplevel.base.surface.events.map.add(&self.map);
@ -123,11 +130,27 @@ pub fn setFocused(self: *View) void {
}
pub fn close(self: *View) void {
self.xdg_toplevel.sendClose();
if(self.focused) {
server.seat.focused_view = null;
}
}
self.xdg_toplevel.sendClose();
pub fn toFullscreen(self: *View) void {
// TODO: What should the final behaviour of this be
if(server.seat.focused_output) |output| {
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.scene_tree.node.reparent(output.layers.content);
}
self.fullscreen = false;
_ = self.xdg_toplevel.setFullscreen(false);
}
pub fn setPosition(self: *View, x: i32, y: i32) void {
@ -258,7 +281,7 @@ fn handleRequestMinimize(
listener: *wl.Listener(void)
) void {
const view: *View = @fieldParentPtr("request_minimize", listener);
server.events.exec("ViewRequestFullscreen", .{view.id});
server.events.exec("ViewRequestMinimize", .{view.id});
}
fn handleSetAppId(

View file

@ -55,3 +55,11 @@ pub fn change_vt(L: *zlua.Lua) i32 {
L.pushNil();
return 1;
}
/// --- Print the scene tree for debugging
pub fn print_scene(L: *zlua.Lua) i32 {
@import("../Debug.zig").debugPrintSceneTree();
L.pushNil();
return 1;
}

View file

@ -1,7 +1,10 @@
const std = @import("std");
const zlua = @import("zlua");
const wlr = @import("wlroots");
const Output = @import("../Output.zig");
const View = @import("../View.zig");
const SceneNodeData = @import("../SceneNodeData.zig").SceneNodeData;
const LuaUtils = @import("LuaUtils.zig");
const server = &@import("../main.zig").server;
@ -15,24 +18,48 @@ fn view_id_err(L: *zlua.Lua) noreturn {
// ---Get the ids for all available views
// ---@return view_id[]?
pub fn get_all_ids(L: *zlua.Lua) i32 {
var it = server.root.scene.tree.children.iterator(.forward);
var index: usize = 1;
var output_it = server.root.output_layout.outputs.iterator(.forward);
var index: i32 = 1;
L.newTable();
while(it.next()) |node| : (index += 1) {
if(node.data == null) continue;
while(output_it.next()) |o| {
if(o.output.data == null) continue;
const output: *Output = @ptrCast(@alignCast(o.output.data.?));
const view = @as(*View, @ptrCast(@alignCast(node.data.?)));
const layers = [_]*wlr.SceneTree{
output.layers.content,
output.layers.fullscreen,
};
L.pushInteger(@intCast(index));
L.pushInteger(@intCast(view.id));
L.setTable(-3);
for(layers) |layer| {
if(layer.children.length() == 0) continue;
if(@intFromPtr(layer) == 0) {
std.log.debug("ts is literally a null ptr", .{});
continue;
}
var view_it = layer.children.iterator(.forward);
while(view_it.next()) |v| {
if(v.data == null) continue;
const scene_node_data: *SceneNodeData = @ptrCast(@alignCast(v.data.?));
if(scene_node_data.* == .view) {
L.pushInteger(@intCast(index));
L.pushInteger(@intCast(scene_node_data.view.id));
L.setTable(-3);
index += 1;
}
}
}
}
return 1;
}
// TODO: What is this?
pub fn check(L: *zlua.Lua) i32 {
L.pushNil();
return 1;
@ -69,14 +96,10 @@ pub fn close(L: *zlua.Lua) i32 {
// ---@param x number x position for view
// ---@param y number y position for view
pub fn set_position(L: *zlua.Lua) i32 {
std.log.debug("repositioning", .{});
const view_id = LuaUtils.coerceInteger(u64, L.checkInteger(1)) catch view_id_err(L);
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", .{});
std.log.debug("position to set: ({d}, {d})", .{x, y});
const view: ?*View = if (view_id == 0) server.seat.focused_view else server.root.viewById(view_id);
if(view) |v| {
v.setPosition(x, y);
@ -158,6 +181,25 @@ pub fn set_focused(L: *zlua.Lua) i32 {
return 1;
}
// ---Resize the view by it's top left corner
// ---@param view_id view_id 0 maps to focused view
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();
}
}
L.pushNil();
return 1;
}
// ---Get the title of the view
// ---@param view_id view_id 0 maps to focused view
// ---@return string?