Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion runtime/master.lua
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ local default_config = {
M.tile_tag = function(tag_id)
local tag = M.state.tags[tag_id]

local area = mez.output.get_available_area(0)
local area = mez.output.get_state(0).available_area

if tag.master == nil then return end

Expand Down
136 changes: 53 additions & 83 deletions src/Output.zig
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const std = @import("std");
const Utils = @import("Utils.zig");

const Server = @import("Server.zig");
const Root = @import("Root.zig");
const View = @import("View.zig");
const LayerSurface = @import("LayerSurface.zig");

Expand All @@ -25,15 +26,12 @@ const Layers = struct {
overlay: *wlr.SceneTree,
};

focused: bool,
id: u64,
fullscreens: std.ArrayList(*View),

wlr_output: *wlr.Output,
state: wlr.Output.State,
tree: *wlr.SceneTree,
scene_node_data: SceneNodeData,
scene_output: *wlr.SceneOutput,
scene_node_data: SceneNodeData,
non_exclusive_area: wlr.Box,

layers: Layers,
Expand All @@ -46,61 +44,48 @@ destroy: wl.Listener(*wlr.Output) = .init(handleDestroy),
pub fn init(wlr_output: *wlr.Output) ?*Output {
errdefer Utils.oomPanic();

if (!wlr_output.initRender(server.allocator, server.renderer)) {
std.log.err("Unable to start output {s}", .{wlr_output.name});
return null;
}

const self = try gpa.create(Output);
errdefer self.deinit();

self.* = .{
.focused = false,
.id = @intFromPtr(wlr_output),
.wlr_output = wlr_output,
.tree = try server.root.scene.tree.createSceneTree(),
.fullscreens = std.ArrayList(*View).initCapacity(gpa, 8) catch Utils.oomPanic(),
.non_exclusive_area = .{ .x = 0, .y = 0, .width = 0, .height = 0 },
.scene_output = try server.root.scene.createSceneOutput(wlr_output),
.scene_node_data = SceneNodeData{ .output = self },

.layers = .{
.background = try self.tree.createSceneTree(),
.bottom = try self.tree.createSceneTree(),
.content = try self.tree.createSceneTree(),
.top = try self.tree.createSceneTree(),
.overlay = try self.tree.createSceneTree(),
.background = try self.scene_output.scene.tree.createSceneTree(),
.bottom = try self.scene_output.scene.tree.createSceneTree(),
.content = try self.scene_output.scene.tree.createSceneTree(),
.top = try self.scene_output.scene.tree.createSceneTree(),
.overlay = try self.scene_output.scene.tree.createSceneTree(),
},

.scene_output = try server.root.scene.createSceneOutput(wlr_output),
.scene_node_data = SceneNodeData{ .output = self },
.state = wlr.Output.State.init()
};

self.wlr_output.data = self;

wlr_output.events.frame.add(&self.frame);
wlr_output.events.request_state.add(&self.request_state);
wlr_output.events.destroy.add(&self.destroy);
wlr_output.events.request_state.add(&self.request_state);

errdefer deinit(self);
var state = wlr.Output.State.init();
defer state.finish();

if (!wlr_output.initRender(server.allocator, server.renderer)) {
std.log.err("Unable to start output {s}", .{wlr_output.name});
return null;
}
if (wlr_output.preferredMode()) |mode| state.setMode(mode);

self.state.setEnabled(true);

if (wlr_output.preferredMode()) |mode| {
self.state.setMode(mode);
}
state.setEnabled(true);

if (!wlr_output.commitState(&self.state)) {
std.log.err("Unable to commit state to output {s}", .{wlr_output.name});
return null;
if (!wlr_output.commitState(&state)) {
std.log.err("Unable to commit state to output {s}", .{ wlr_output.name });
}

// TODO: Allow user to define output positions
const layout_output = try server.root.output_layout.addAuto(self.wlr_output);
server.root.scene_output_layout.addOutput(layout_output, self.scene_output);
self.arrangeLayers();

self.setFocused();

self.wlr_output.data = self;
self.tree.node.data = &self.scene_node_data;

server.events.exec("OutputInitPost", .{self.id}, "After a new output is initialized. You're probably looking for OutputStateChange.");

return self;
Expand All @@ -112,23 +97,15 @@ pub fn deinit(self: *Output) void {
self.frame.link.remove();
self.request_state.link.remove();
self.destroy.link.remove();

self.state.finish();

self.wlr_output.destroy();

server.events.exec("OutputDeinitPost", .{}, "After an output is de-initialized.");

gpa.destroy(self);

server.events.exec("OutputDeinitPost", .{}, "After an output is de-initialized.");
}

pub fn setFocused(self: *Output) void {
if (server.getDefaultSeat().focused_output) |prev_output| {
prev_output.focused = false;
}

server.getDefaultSeat().focused_output = self;
self.focused = true;
}

const SurfaceAtResult = struct {
Expand Down Expand Up @@ -205,49 +182,36 @@ fn handleRequestState(
listener: *wl.Listener(*wlr.Output.event.RequestState),
event: *wlr.Output.event.RequestState,
) void {
const output: *Output = @fieldParentPtr("request_state", listener);
const self: *Output = @fieldParentPtr("request_state", listener);

if (!output.wlr_output.commitState(event.state)) {
if (!self.wlr_output.commitState(event.state)) {
std.log.warn("failed to set output state {}", .{event.state});
// nothing should've changed, so we don't do anything
return;
}

// update the config with all monitors and send it to the output_manager
const config = wlr.OutputConfigurationV1.create() catch Utils.oomPanic();
var iter = server.root.scene.outputs.iterator(.forward);
while (iter.next()) |out| {
_ = wlr.OutputConfigurationV1.Head.create(config, out.output) catch Utils.oomPanic();
}
server.root.output_manager.setConfiguration(config);

// make sure the layers are behaving
arrangeLayers(output);
Root.configureOutputs(&server.root);
self.arrangeLayers();

server.events.exec("OutputStateChange", .{output.id}, "After an outputs state has been changed.");
server.events.exec("OutputStateChange", .{self.id}, "After an outputs state has been changed.");
}

fn handleFrame(_: *wl.Listener(*wlr.Output), wlr_output: *wlr.Output) void {
const scene_output = server.root.scene.getSceneOutput(wlr_output);
fn handleFrame(listener: *wl.Listener(*wlr.Output), _: *wlr.Output) void {
const self: *Output = @fieldParentPtr("frame", listener);

if (scene_output == null) {
std.log.err("Unable to get scene output to render", .{});
return;
if (!self.scene_output.commit(null)) {
std.log.warn("setting output state failed for output: {}", .{ self.id });
}

// std.log.info("Rendering commited scene output\n", .{});
_ = scene_output.?.commit(null);

var now = posix.clock_gettime(posix.CLOCK.MONOTONIC) catch @panic("CLOCK_MONOTONIC not supported");
scene_output.?.sendFrameDone(&now);
var now = posix.clock_gettime(posix.CLOCK.MONOTONIC) catch {
std.debug.panic("CLOCK_MONOTONIC not supported", .{});
};
self.scene_output.sendFrameDone(&now);
}

fn handleDestroy(listener: *wl.Listener(*wlr.Output), _: *wlr.Output) void {
std.log.debug("Handling destroy", .{});
const output: *Output = @fieldParentPtr("destroy", listener);

std.log.debug("removing output: {s}", .{output.wlr_output.name});

output.frame.link.remove();
output.request_state.link.remove();
output.destroy.link.remove();
Expand All @@ -269,19 +233,18 @@ 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);
var it = layer.children.iterator(.forward);

while (it.next()) |node| {
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.output.wlr_output != self.wlr_output) continue;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't this mean that a layer surface is currently a child of the wrong output and this is an incorrect state? Perhaps fix it or error?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe, I think we need to do some testing to figure out what would be best. I'll add a todo to the codebase.

if (!layer_surface.wlr_layer_surface.initialized) continue;

// TEST: river seems to try and prevent clients from taking an
Expand All @@ -291,13 +254,20 @@ pub fn arrangeLayers(self: *Output) void {

layer_surface.scene_layer_surface.configure(
&full_box,
&self.non_exclusive_area,
&self.non_exclusive_area
);

// TEST: are these calls useless?
// 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);
// set the position of the new layersurface relative to the output
// it belongs to
const x = layer_surface.output.scene_output.x;
const y = layer_surface.output.scene_output.y;
layer_surface.scene_layer_surface.tree.node.setPosition(x, y);
layer_surface.scene_layer_surface.tree.node.subsurfaceTreeSetClip(&.{
.x = 0,
.y = 0,
.width = layer_surface.output.wlr_output.width,
.height = layer_surface.output.wlr_output.height,
});
}
}
}
22 changes: 20 additions & 2 deletions src/Root.zig
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ const Utils = @import("Utils.zig");
scene_node_data: SceneNode.Data,

scene: *wlr.Scene,
scene_output_layout: *wlr.SceneOutputLayout,
output_layout: *wlr.OutputLayout,
output_manager: *wlr.OutputManagerV1,
output_power_manager: *wlr.OutputPowerManagerV1,
Expand All @@ -45,7 +44,6 @@ pub fn init(self: *Root) void {
.output_manager = try wlr.OutputManagerV1.create(server.wl_server),
.output_power_manager = try wlr.OutputPowerManagerV1.create(server.wl_server),
.output_layout = output_layout,
.scene_output_layout = try scene.attachOutputLayout(output_layout),
};

if (server.linux_dmabuf) |dmabuf| self.scene.setLinuxDmabufV1(dmabuf);
Expand Down Expand Up @@ -75,6 +73,26 @@ pub fn deinit(self: *Root) void {
self.scene.tree.node.destroy();
}

pub fn configureOutputs(self: *const Root) void {
// update the config with all monitors and send it to the output_manager
const config = wlr.OutputConfigurationV1.create() catch Utils.oomPanic();

// TODO: do we ommit disabled monitors here?
var iter = self.scene.outputs.iterator(.forward);
while (iter.next()) |scene_output| {
const config_head = wlr.OutputConfigurationV1.Head.create(config, scene_output.output) catch Utils.oomPanic();

if (self.output_layout.get(scene_output.output)) |_| {
_ = self.output_layout.addAuto(scene_output.output) catch Utils.oomPanic();
}

config_head.state.x = scene_output.x;
config_head.state.y = scene_output.y;
}

self.output_manager.setConfiguration(config);
}

// Search output_layout's outputs, and each outputs views
pub fn viewById(self: *Root, id: u64) ?*View {
var output_it = self.output_layout.outputs.iterator(.forward);
Expand Down
4 changes: 0 additions & 4 deletions src/Seat.zig
Original file line number Diff line number Diff line change
Expand Up @@ -169,10 +169,6 @@ pub fn focusSurface(self: *Seat, to_focus: ?FocusData) void {
}

pub fn focusOutput(self: *Seat, output: *Output) void {
if (self.focused_output) |prev_output| {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like not having to set the state of individual outputs, good stuff

prev_output.focused = false;
}

self.focused_output = output;
}

Expand Down
26 changes: 24 additions & 2 deletions src/Server.zig
Original file line number Diff line number Diff line change
Expand Up @@ -286,8 +286,30 @@ fn handleNewInput(listener: *wl.Listener(*wlr.InputDevice), device: *wlr.InputDe
});
}

fn handleNewOutput(_: *wl.Listener(*wlr.Output), wlr_output: *wlr.Output) void {
_ = Output.init(wlr_output);
fn handleNewOutput(listener: *wl.Listener(*wlr.Output), wlr_output: *wlr.Output) void {
const self: *Server = @fieldParentPtr("new_output", listener);
const output = Output.init(wlr_output) orelse {
std.log.err("Failed to create new output", .{});
return;
};

// TODO: Allow user to define output positions

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps to do this we could use an autocommand that simply returns the output's position? Just a though, no need to do this to merge

const layout_output = self.root.output_layout.addAuto(output.wlr_output) catch {
std.log.err("failed to add output to the output layout", .{});
return;
};

output.scene_output.setPosition(layout_output.x, layout_output.y);

// FIXME: without this the lua api can crash mez very easily. Thankfully we
// don't have a case for not having any output selected, but it'd still be
// better if we didn't crash.
if (self.getDefaultSeat().focused_output == null) {
self.getDefaultSeat().focusOutput(output);
}

Root.configureOutputs(&self.root);
output.arrangeLayers();
}

fn handleNewXdgToplevel(_: *wl.Listener(*wlr.XdgToplevel), xdg_toplevel: *wlr.XdgToplevel) void {
Expand Down
7 changes: 4 additions & 3 deletions src/View.zig
Original file line number Diff line number Diff line change
Expand Up @@ -275,9 +275,10 @@ fn handleUnmap(listener: *wl.Listener(void)) void {

server.events.exec("ViewUnmapPre", .{view.id}, "Before the view is unmapped. This view is still currently visibile to the user.");

if (server.getDefaultSeat().focused_surface) |fs| {
if (fs == .view and fs.view == view) {
server.getDefaultSeat().focusSurface(null);
var iter = server.seats.iterator(.forward);
while (iter.next()) |seat| {
if (seat.focused_surface) |fs| {
if (fs == .view and fs.view == view) seat.focusSurface(null);
}
}

Expand Down
Loading