Implement infrastructure for tags
This commit is contained in:
parent
5020106b93
commit
42d454b26f
3 changed files with 159 additions and 73 deletions
|
@ -84,6 +84,12 @@ pub const Output = struct {
|
|||
var it = output.root.views.last;
|
||||
while (it) |node| : (it = node.prev) {
|
||||
const view = &node.data;
|
||||
|
||||
// Only render currently visible views
|
||||
if (!view.isVisible(output.root.current_focused_tags)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: remove this check and move unmaped views back to unmaped TailQueue
|
||||
if (!view.mapped) {
|
||||
// An unmapped view should not be rendered.
|
||||
|
|
169
src/root.zig
169
src/root.zig
|
@ -22,6 +22,10 @@ pub const Root = struct {
|
|||
|
||||
focused_view: ?*View,
|
||||
|
||||
/// A bit field of focused tags
|
||||
current_focused_tags: u32,
|
||||
pending_focused_tags: ?u32,
|
||||
|
||||
/// Number of views in "master" section of the screen.
|
||||
master_count: u32,
|
||||
|
||||
|
@ -30,7 +34,7 @@ pub const Root = struct {
|
|||
|
||||
// Number of pending configures sent in the current transaction.
|
||||
// A value of 0 means there is no current transaction.
|
||||
pending_count: u32,
|
||||
pending_configures: u32,
|
||||
|
||||
/// Handles timeout of transactions
|
||||
transaction_timer: ?*c.wl_event_source,
|
||||
|
@ -51,10 +55,16 @@ pub const Root = struct {
|
|||
|
||||
self.focused_view = null;
|
||||
|
||||
self.current_focused_tags = 1 << 0;
|
||||
self.pending_focused_tags = null;
|
||||
|
||||
self.master_count = 1;
|
||||
|
||||
self.master_factor = 0.6;
|
||||
|
||||
self.pending_count = 0;
|
||||
self.pending_configures = 0;
|
||||
|
||||
self.transaction_timer = null;
|
||||
}
|
||||
|
||||
pub fn destroy(self: Self) void {
|
||||
|
@ -70,7 +80,7 @@ pub const Root = struct {
|
|||
|
||||
pub fn addView(self: *Self, wlr_xdg_surface: *c.wlr_xdg_surface) void {
|
||||
const node = self.views.allocateNode(self.server.allocator) catch unreachable;
|
||||
node.data.init(self, wlr_xdg_surface);
|
||||
node.data.init(self, wlr_xdg_surface, self.current_focused_tags);
|
||||
self.unmapped_views.prepend(node);
|
||||
}
|
||||
|
||||
|
@ -86,53 +96,87 @@ pub const Root = struct {
|
|||
return null;
|
||||
}
|
||||
|
||||
/// Focus the next view in the stack, wrapping if needed. Does nothing
|
||||
/// if there is only one view in the stack.
|
||||
/// Focus the next visible view in the stack, wrapping if needed. Does
|
||||
/// nothing if there is only one view in the stack.
|
||||
pub fn focusNextView(self: Self) void {
|
||||
if (self.focused_view) |current_focus| {
|
||||
// If there is a currently focused view, focus the next view in the stack.
|
||||
const node = @fieldParentPtr(std.TailQueue(View).Node, "data", current_focus);
|
||||
if (node.next) |next_node| {
|
||||
const view = &next_node.data;
|
||||
// If there is a currently focused view, focus the next visible view in the stack.
|
||||
var it = @fieldParentPtr(std.TailQueue(View).Node, "data", current_focus).next;
|
||||
while (it) |node| : (it = node.next) {
|
||||
const view = &node.data;
|
||||
if (view.isVisible(self.current_focused_tags)) {
|
||||
view.focus(view.wlr_xdg_surface.surface);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// There is either no currently focused view or the last visible view in the
|
||||
// stack is focused and we need to wrap.
|
||||
var it = self.views.first;
|
||||
while (it) |node| : (it = node.next) {
|
||||
const view = &node.data;
|
||||
if (view.isVisible(self.current_focused_tags)) {
|
||||
view.focus(view.wlr_xdg_surface.surface);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// There is either no currently focused view or the last view in the
|
||||
// stack is focused and we need to wrap.
|
||||
if (self.views.first) |first_node| {
|
||||
const view = &first_node.data;
|
||||
view.focus(view.wlr_xdg_surface.surface);
|
||||
}
|
||||
}
|
||||
|
||||
/// Focus the previous view in the stack, wrapping if needed. Does nothing
|
||||
/// if there is only one view in the stack.
|
||||
pub fn focusPrevView(self: Self) void {
|
||||
if (self.focused_view) |current_focus| {
|
||||
// If there is a currently focused view, focus the previous view in the stack.
|
||||
const node = @fieldParentPtr(std.TailQueue(View).Node, "data", current_focus);
|
||||
if (node.prev) |prev_node| {
|
||||
const view = &prev_node.data;
|
||||
// If there is a currently focused view, focus the previous visible view in the stack.
|
||||
var it = @fieldParentPtr(std.TailQueue(View).Node, "data", current_focus).prev;
|
||||
while (it) |node| : (it = node.prev) {
|
||||
const view = &node.data;
|
||||
if (view.isVisible(self.current_focused_tags)) {
|
||||
view.focus(view.wlr_xdg_surface.surface);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// There is either no currently focused view or the first visible view in the
|
||||
// stack is focused and we need to wrap.
|
||||
var it = self.views.last;
|
||||
while (it) |node| : (it = node.prev) {
|
||||
const view = &node.data;
|
||||
if (view.isVisible(self.current_focused_tags)) {
|
||||
view.focus(view.wlr_xdg_surface.surface);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// There is either no currently focused view or the first view in the
|
||||
// stack is focused and we need to wrap.
|
||||
if (self.views.last) |last_node| {
|
||||
const view = &last_node.data;
|
||||
view.focus(view.wlr_xdg_surface.surface);
|
||||
}
|
||||
|
||||
// TODO: obsolete this function by using a better data structure
|
||||
pub fn visibleCount(self: Self, tags: u32) u32 {
|
||||
var count: u32 = 0;
|
||||
var it = self.views.first;
|
||||
while (it) |node| : (it = node.next) {
|
||||
const view = &node.data;
|
||||
if (view.isVisible(tags)) {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
pub fn arrange(self: *Self) void {
|
||||
if (self.views.len == 0) {
|
||||
const tags = if (self.pending_focused_tags) |tags|
|
||||
tags
|
||||
else
|
||||
self.current_focused_tags;
|
||||
|
||||
const visible_count = self.visibleCount(tags);
|
||||
|
||||
if (visible_count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const master_count = util.min(u32, self.master_count, @intCast(u32, self.views.len));
|
||||
const slave_count = if (master_count >= self.views.len) 0 else @intCast(u32, self.views.len) - master_count;
|
||||
const master_count = util.min(u32, self.master_count, visible_count);
|
||||
const slave_count = if (master_count >= visible_count) 0 else visible_count - master_count;
|
||||
|
||||
// This can't return null if we pass null as the reference
|
||||
const output_box: *c.wlr_box = c.wlr_output_layout_get_box(self.wlr_output_layout, null);
|
||||
|
@ -152,39 +196,47 @@ pub const Root = struct {
|
|||
|
||||
var i: u32 = 0;
|
||||
var it = self.views.first;
|
||||
while (it) |node| : ({
|
||||
i += 1;
|
||||
it = node.next;
|
||||
}) {
|
||||
if (i < master_count) {
|
||||
const view = &node.data;
|
||||
while (it) |node| : (it = node.next) {
|
||||
const view = &node.data;
|
||||
|
||||
if (!view.isVisible(tags)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i < master_count) {
|
||||
// Add the remainder to the first master to ensure every pixel of height is used
|
||||
const master_height = @divTrunc(@intCast(u32, output_box.height), master_count);
|
||||
const master_height_rem = @intCast(u32, output_box.height) % master_count;
|
||||
|
||||
view.pending_state.x = 0;
|
||||
view.pending_state.y = @intCast(i32, i * master_height +
|
||||
if (i > 0) master_height_rem else 0);
|
||||
view.pending_state = View.State{
|
||||
.x = 0,
|
||||
.y = @intCast(i32, i * master_height +
|
||||
if (i > 0) master_height_rem else 0),
|
||||
|
||||
view.pending_state.width = master_column_width;
|
||||
view.pending_state.height = master_height +
|
||||
if (i == 0) master_height_rem else 0;
|
||||
.width = master_column_width,
|
||||
.height = master_height + if (i == 0) master_height_rem else 0,
|
||||
|
||||
.tags = view.current_state.tags,
|
||||
};
|
||||
} else {
|
||||
const view = &node.data;
|
||||
|
||||
// Add the remainder to the first slave to ensure every pixel of height is used
|
||||
const slave_height = @divTrunc(@intCast(u32, output_box.height), slave_count);
|
||||
const slave_height_rem = @intCast(u32, output_box.height) % slave_count;
|
||||
|
||||
view.pending_state.x = @intCast(i32, master_column_width);
|
||||
view.pending_state.y = @intCast(i32, (i - master_count) * slave_height +
|
||||
if (i > master_count) slave_height_rem else 0);
|
||||
view.pending_state = View.State{
|
||||
.x = @intCast(i32, master_column_width),
|
||||
.y = @intCast(i32, (i - master_count) * slave_height +
|
||||
if (i > master_count) slave_height_rem else 0),
|
||||
|
||||
view.pending_state.width = slave_column_width;
|
||||
view.pending_state.height = slave_height +
|
||||
if (i == master_count) slave_height_rem else 0;
|
||||
.width = slave_column_width,
|
||||
.height = slave_height +
|
||||
if (i == master_count) slave_height_rem else 0,
|
||||
|
||||
.tags = view.current_state.tags,
|
||||
};
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
self.startTransaction();
|
||||
|
@ -195,7 +247,7 @@ pub const Root = struct {
|
|||
fn startTransaction(self: *Self) void {
|
||||
// If a new transaction is started while another is in progress, we need
|
||||
// to reset the pending count to 0 and clear serials from the views
|
||||
self.pending_count = 0;
|
||||
self.pending_configures = 0;
|
||||
|
||||
var it = self.views.first;
|
||||
while (it) |node| : (it = node.next) {
|
||||
|
@ -206,7 +258,7 @@ pub const Root = struct {
|
|||
|
||||
if (view.needsConfigure()) {
|
||||
view.configurePending();
|
||||
self.pending_count += 1;
|
||||
self.pending_configures += 1;
|
||||
|
||||
// We save the current buffer, so we can send an early
|
||||
// frame done event to give the client a head start on
|
||||
|
@ -221,7 +273,7 @@ pub const Root = struct {
|
|||
}
|
||||
}
|
||||
|
||||
if (self.pending_count > 0) {
|
||||
if (self.pending_configures > 0) {
|
||||
// TODO: log failure to create timer and commit immediately
|
||||
self.transaction_timer = c.wl_event_loop_add_timer(
|
||||
self.server.wl_event_loop,
|
||||
|
@ -245,8 +297,8 @@ pub const Root = struct {
|
|||
}
|
||||
|
||||
pub fn notifyConfigured(self: *Self) void {
|
||||
self.pending_count -= 1;
|
||||
if (self.pending_count == 0) {
|
||||
self.pending_configures -= 1;
|
||||
if (self.pending_configures == 0) {
|
||||
self.commitTransaction();
|
||||
}
|
||||
}
|
||||
|
@ -259,7 +311,13 @@ pub const Root = struct {
|
|||
// TODO: apply damage properly
|
||||
|
||||
// Ensure this is set to 0 to avoid entering invalid state (e.g. if called due to timeout)
|
||||
self.pending_count = 0;
|
||||
self.pending_configures = 0;
|
||||
|
||||
// If there were pending focused tags, make them the current focus
|
||||
if (self.pending_focused_tags) |tags| {
|
||||
self.current_focused_tags = tags;
|
||||
self.pending_focused_tags = null;
|
||||
}
|
||||
|
||||
var it = self.views.first;
|
||||
while (it) |node| : (it = node.next) {
|
||||
|
@ -267,7 +325,10 @@ pub const Root = struct {
|
|||
|
||||
// Ensure that all pending state is cleared
|
||||
view.pending_serial = null;
|
||||
view.current_state = view.pending_state;
|
||||
if (view.pending_state) |state| {
|
||||
view.current_state = state;
|
||||
view.pending_state = null;
|
||||
}
|
||||
view.dropStashedBuffer();
|
||||
}
|
||||
}
|
||||
|
|
57
src/view.zig
57
src/view.zig
|
@ -3,13 +3,6 @@ const c = @import("c.zig");
|
|||
|
||||
const Root = @import("root.zig").Root;
|
||||
|
||||
pub const ViewState = struct {
|
||||
x: i32,
|
||||
y: i32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
};
|
||||
|
||||
pub const View = struct {
|
||||
const Self = @This();
|
||||
|
||||
|
@ -18,9 +11,16 @@ pub const View = struct {
|
|||
|
||||
mapped: bool,
|
||||
|
||||
current_state: ViewState,
|
||||
// TODO: make this a ?ViewState
|
||||
pending_state: ViewState,
|
||||
pub const State = struct {
|
||||
x: i32,
|
||||
y: i32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
tags: u32,
|
||||
};
|
||||
|
||||
current_state: State,
|
||||
pending_state: ?State,
|
||||
|
||||
pending_serial: ?u32,
|
||||
|
||||
|
@ -34,7 +34,7 @@ pub const View = struct {
|
|||
// listen_request_move: c.wl_listener,
|
||||
// listen_request_resize: c.wl_listener,
|
||||
|
||||
pub fn init(self: *Self, root: *Root, wlr_xdg_surface: *c.wlr_xdg_surface) void {
|
||||
pub fn init(self: *Self, root: *Root, wlr_xdg_surface: *c.wlr_xdg_surface, tags: u32) void {
|
||||
self.root = root;
|
||||
self.wlr_xdg_surface = wlr_xdg_surface;
|
||||
|
||||
|
@ -44,12 +44,18 @@ pub const View = struct {
|
|||
c.WLR_EDGE_RIGHT | c.WLR_EDGE_TOP | c.WLR_EDGE_BOTTOM);
|
||||
|
||||
self.mapped = false;
|
||||
self.current_state = ViewState{
|
||||
|
||||
self.current_state = State{
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.height = 0,
|
||||
.width = 0,
|
||||
.tags = tags,
|
||||
};
|
||||
self.pending_state = null;
|
||||
|
||||
self.pending_serial = null;
|
||||
|
||||
self.stashed_buffer = null;
|
||||
|
||||
self.listen_map.notify = handleMap;
|
||||
|
@ -70,16 +76,24 @@ pub const View = struct {
|
|||
}
|
||||
|
||||
pub fn needsConfigure(self: Self) bool {
|
||||
return self.pending_state.width != self.current_state.width or
|
||||
self.pending_state.height != self.current_state.height;
|
||||
if (self.pending_state) |pending_state| {
|
||||
return pending_state.width != self.current_state.width or
|
||||
pending_state.height != self.current_state.height;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn configurePending(self: *Self) void {
|
||||
self.pending_serial = c.wlr_xdg_toplevel_set_size(
|
||||
self.wlr_xdg_surface,
|
||||
self.pending_state.width,
|
||||
self.pending_state.height,
|
||||
);
|
||||
if (self.pending_state) |pending_state| {
|
||||
self.pending_serial = c.wlr_xdg_toplevel_set_size(
|
||||
self.wlr_xdg_surface,
|
||||
pending_state.width,
|
||||
pending_state.height,
|
||||
);
|
||||
} else {
|
||||
// TODO: log warning
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sendFrameDone(self: Self) void {
|
||||
|
@ -119,6 +133,11 @@ pub const View = struct {
|
|||
view.root.arrange();
|
||||
}
|
||||
|
||||
/// Returns true if the view is shown given the current state of tags
|
||||
pub fn isVisible(self: Self, tags: u32) bool {
|
||||
return tags & self.current_state.tags != 0;
|
||||
}
|
||||
|
||||
fn handleUnmap(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
|
||||
const view = @fieldParentPtr(View, "listen_unmap", listener.?);
|
||||
const root = view.root;
|
||||
|
|
Loading…
Reference in a new issue