diff --git a/river/Output.zig b/river/Output.zig index f1f4419..7dc10cf 100644 --- a/river/Output.zig +++ b/river/Output.zig @@ -181,7 +181,7 @@ fn layoutFull(self: *Self, visible_count: u32, output_tags: u32) void { var it = ViewStack(View).pendingIterator(self.views.first, output_tags); while (it.next()) |node| { const view = &node.view; - if (view.mode == .layout) view.pending_box = full_box; + if (view.pending.mode == .layout) view.pending.box = full_box; } } @@ -286,8 +286,8 @@ fn layoutExternal(self: *Self, visible_count: u32, output_tags: u32) !void { var view_it = ViewStack(View).pendingIterator(self.views.first, output_tags); while (view_it.next()) |node| { const view = &node.view; - if (view.mode == .layout) { - view.pending_box = view_boxen.items[i]; + if (view.pending.mode == .layout) { + view.pending.box = view_boxen.items[i]; i += 1; } } @@ -310,7 +310,7 @@ pub fn arrangeViews(self: *Self) void { var count: u32 = 0; var it = ViewStack(View).pendingIterator(self.views.first, output_tags); while (it.next()) |node| { - if (node.view.mode == .layout) count += 1; + if (node.view.pending.mode == .layout) count += 1; } break :blk count; }; diff --git a/river/OutputStatus.zig b/river/OutputStatus.zig index 980dbe8..be0568f 100644 --- a/river/OutputStatus.zig +++ b/river/OutputStatus.zig @@ -62,7 +62,7 @@ pub fn sendViewTags(self: Self) void { var it = ViewStack(View).iterator(self.output.views.first, std.math.maxInt(u32)); while (it.next()) |node| - view_tags.append(node.view.current_tags) catch { + view_tags.append(node.view.current.tags) catch { c.wl_resource_post_no_memory(self.wl_resource); log.crit(.river_status, "out of memory", .{}); return; diff --git a/river/Root.zig b/river/Root.zig index 7bcb83e..81478c8 100644 --- a/river/Root.zig +++ b/river/Root.zig @@ -175,7 +175,7 @@ fn startTransaction(self: *Self) void { fn handleTimeout(data: ?*c_void) callconv(.C) c_int { const self = util.voidCast(Self, data.?); - log.err(.transaction, "time out occurred, some imperfect frames may be shown", .{}); + log.err(.transaction, "timeout occurred, some imperfect frames may be shown", .{}); self.commitTransaction(); @@ -225,19 +225,10 @@ fn commitTransaction(self: *Self) void { var view_it = ViewStack(View).iterator(output.views.first, std.math.maxInt(u32)); while (view_it.next()) |view_node| { const view = &view_node.view; - // Ensure that all pending state is cleared + // Apply pending state view.pending_serial = null; - if (view.pending_box) |state| { - view.current_box = state; - view.pending_box = null; - } - - // Apply possible pending tags - if (view.pending_tags) |tags| { - view.current_tags = tags; - view.pending_tags = null; - view_tags_changed = true; - } + if (view.pending.tags != view.current.tags) view_tags_changed = true; + view.current = view.pending; view.dropSavedBuffers(); } @@ -247,7 +238,5 @@ fn commitTransaction(self: *Self) void { // Iterate over all seats and update focus var it = self.server.input_manager.seats.first; - while (it) |seat_node| : (it = seat_node.next) { - seat_node.data.focus(null); - } + while (it) |seat_node| : (it = seat_node.next) seat_node.data.focus(null); } diff --git a/river/Seat.zig b/river/Seat.zig index cdc08ed..f67fbae 100644 --- a/river/Seat.zig +++ b/river/Seat.zig @@ -125,7 +125,7 @@ pub fn focus(self: *Self, _view: ?*View) void { // If view is null or not currently visible if (if (view) |v| v.output != self.focused_output or - v.current_tags & self.focused_output.current_focused_tags == 0 + v.current.tags & self.focused_output.current_focused_tags == 0 else true) { // Set view to the first currently visible view on in the focus stack if any diff --git a/river/View.zig b/river/View.zig index a50957e..ffa7e4e 100644 --- a/river/View.zig +++ b/river/View.zig @@ -41,6 +41,18 @@ const Mode = enum { float, }; +const State = struct { + /// The output-relative coordinates and dimensions of the view. The + /// surface itself may have other dimensions which are stored in the + /// surface_box member. + box: Box, + + /// The tags of the view, as a bitmask + tags: u32, + + mode: Mode, +}; + const SavedBuffer = struct { wlr_buffer: *c.wlr_buffer, box: Box, @@ -56,19 +68,15 @@ output: *Output, /// This is non-null exactly when the view is mapped wlr_surface: ?*c.wlr_surface, -/// The current mode of the view -mode: Mode, - /// True if the view is currently focused by at least one seat focused: bool, -/// The current output-relative coordinates and dimensions of the view. The -/// surface itself may have other dimensions which are stored in the -/// surface_box member. -current_box: Box, +/// The double-buffered state of the view +current: State, +pending: State, -/// Pending dimensions of the view during a transaction -pending_box: ?Box, +/// The serial sent with the currently pending configure event +pending_serial: ?u32, /// The currently commited geometry of the surface. The x/y may be negative if /// for example the client has decided to draw CSD shadows a la GTK. @@ -85,29 +93,24 @@ saved_buffers: std.ArrayList(SavedBuffer), natural_width: u32, natural_height: u32, -current_tags: u32, -pending_tags: ?u32, - -pending_serial: ?u32, - pub fn init(self: *Self, output: *Output, tags: u32, surface: var) void { self.output = output; self.wlr_surface = null; - self.mode = .layout; self.focused = false; - self.current_box = Box{ - .x = 0, - .y = 0, - .height = 0, - .width = 0, + self.current = .{ + .box = .{ + .x = 0, + .y = 0, + .height = 0, + .width = 0, + }, + .tags = tags, + .mode = .layout, }; - self.pending_box = null; - - self.current_tags = tags; - self.pending_tags = null; + self.pending = self.current; self.pending_serial = null; @@ -135,13 +138,9 @@ pub fn needsConfigure(self: Self) bool { } pub fn configure(self: Self) void { - if (self.pending_box) |pending_box| { - switch (self.impl) { - .xdg_toplevel => |xdg_toplevel| xdg_toplevel.configure(pending_box), - .xwayland_view => |xwayland_view| xwayland_view.configure(pending_box), - } - } else { - log.err(.transaction, "configure called on a View with no pending box", .{}); + switch (self.impl) { + .xdg_toplevel => |xdg_toplevel| xdg_toplevel.configure(self.pending.box), + .xwayland_view => |xwayland_view| xwayland_view.configure(self.pending.box), } } @@ -190,7 +189,8 @@ fn saveBuffersIterator( } } -/// Set the focued bool and the active state of the view if it is a toplevel +/// Set the focused bool and the active state of the view if it is a toplevel +/// TODO: This is insufficient for multi-seat, probably need a focus counter. pub fn setFocused(self: *Self, focused: bool) void { self.focused = focused; switch (self.impl) { @@ -199,30 +199,6 @@ pub fn setFocused(self: *Self, focused: bool) void { } } -/// Set the mode of the view to the given mode -pub fn setMode(self: *Self, mode: Mode) void { - switch (self.mode) { - .layout => switch (mode) { - .layout => {}, - .float => { - self.mode = .float; - self.pending_box = Box{ - .x = std.math.max(0, @divTrunc(@intCast(i32, self.output.usable_box.width) - - @intCast(i32, self.natural_width), 2)), - .y = std.math.max(0, @divTrunc(@intCast(i32, self.output.usable_box.height) - - @intCast(i32, self.natural_height), 2)), - .width = self.natural_width, - .height = self.natural_height, - }; - }, - }, - .float => switch (mode) { - .float => {}, - .layout => self.mode = .layout, - }, - } -} - /// Move a view from one output to another, sending the required enter/leave /// events. pub fn sendToOutput(self: *Self, destination_output: *Output) void { diff --git a/river/XdgToplevel.zig b/river/XdgToplevel.zig index dbbcb9d..b08e8bd 100644 --- a/river/XdgToplevel.zig +++ b/river/XdgToplevel.zig @@ -62,8 +62,6 @@ pub fn init(self: *Self, view: *View, wlr_xdg_surface: *c.wlr_xdg_surface) void /// Returns true if a configure must be sent to ensure the dimensions of the /// pending_box are applied. pub fn needsConfigure(self: Self) bool { - const pending_box = self.view.pending_box orelse return false; - const wlr_xdg_toplevel: *c.wlr_xdg_toplevel = @field( self.wlr_xdg_surface, c.wlr_xdg_surface_union, @@ -73,8 +71,8 @@ pub fn needsConfigure(self: Self) bool { // sync with the current dimensions or be the dimensions sent with the // most recent configure. In both cases server_pending has the values we // want to check against. - return pending_box.width != wlr_xdg_toplevel.server_pending.width or - pending_box.height != wlr_xdg_toplevel.server_pending.height; + return self.view.pending.box.width != wlr_xdg_toplevel.server_pending.width or + self.view.pending.box.height != wlr_xdg_toplevel.server_pending.height; } /// Send a configure event, applying the width/height of the pending box. @@ -109,8 +107,8 @@ pub fn surfaceAt(self: Self, ox: f64, oy: f64, sx: *f64, sy: *f64) ?*c.wlr_surfa const view = self.view; return c.wlr_xdg_surface_surface_at( self.wlr_xdg_surface, - ox - @intToFloat(f64, view.current_box.x - view.surface_box.x), - oy - @intToFloat(f64, view.current_box.y - view.surface_box.y), + ox - @intToFloat(f64, view.current.box.x - view.surface_box.x), + oy - @intToFloat(f64, view.current.box.y - view.surface_box.y), sx, sy, ); @@ -171,7 +169,15 @@ fn handleMap(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void { for (root.server.config.float_filter.items) |filter_app_id| { // Make views with app_ids listed in the float filter float if (std.mem.eql(u8, std.mem.span(app_id), std.mem.span(filter_app_id))) { - view.setMode(.float); + view.pending.mode = .float; + view.pending.box = .{ + .x = std.math.max(0, @divTrunc(@intCast(i32, view.output.usable_box.width) - + @intCast(i32, view.natural_width), 2)), + .y = std.math.max(0, @divTrunc(@intCast(i32, view.output.usable_box.height) - + @intCast(i32, view.natural_height), 2)), + .width = view.natural_width, + .height = view.natural_height, + }; break; } } else if ((wlr_xdg_toplevel.parent != null) or @@ -179,7 +185,15 @@ fn handleMap(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void { (state.min_width == state.max_width or state.min_height == state.max_height))) { // If the toplevel has a parent or is of fixed size make it float - view.setMode(.float); + view.pending.mode = .float; + view.pending.box = .{ + .x = std.math.max(0, @divTrunc(@intCast(i32, view.output.usable_box.width) - + @intCast(i32, view.natural_width), 2)), + .y = std.math.max(0, @divTrunc(@intCast(i32, view.output.usable_box.height) - + @intCast(i32, view.natural_height), 2)), + .width = view.natural_width, + .height = view.natural_height, + }; } // If the toplevel has no parent, inform it that it is tiled. This @@ -246,5 +260,5 @@ fn handleNewPopup(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void { // This will free itself on destroy var xdg_popup = util.gpa.create(XdgPopup) catch unreachable; - xdg_popup.init(self.view.output, &self.view.current_box, wlr_xdg_popup); + xdg_popup.init(self.view.output, &self.view.current.box, wlr_xdg_popup); } diff --git a/river/XwaylandView.zig b/river/XwaylandView.zig index 6ac5840..1cd0143 100644 --- a/river/XwaylandView.zig +++ b/river/XwaylandView.zig @@ -57,11 +57,8 @@ pub fn init(self: *Self, view: *View, wlr_xwayland_surface: *c.wlr_xwayland_surf } pub fn needsConfigure(self: Self) bool { - const view = self.view; - if (view.pending_box) |pending_box| - return view.current_box.width != pending_box.width or - view.current_box.height != pending_box.height; - return false; + return self.view.current.box.width != self.view.pending.box.width or + self.view.current.box.height != self.view.pending.box.height; } /// Tell the client to take a new size @@ -105,8 +102,8 @@ pub fn forEachSurface( pub fn surfaceAt(self: Self, ox: f64, oy: f64, sx: *f64, sy: *f64) ?*c.wlr_surface { return c.wlr_surface_surface_at( self.wlr_xwayland_surface.surface, - ox - @intToFloat(f64, self.view.current_box.x), - oy - @intToFloat(f64, self.view.current_box.y), + ox - @intToFloat(f64, self.view.current.box.x), + oy - @intToFloat(f64, self.view.current.box.y), sx, sy, ); diff --git a/river/command/tags.zig b/river/command/tags.zig index 48aaa28..5c14aaa 100644 --- a/river/command/tags.zig +++ b/river/command/tags.zig @@ -43,10 +43,8 @@ pub fn setViewTags( ) Error!void { const tags = try parseTags(allocator, args, out); if (seat.focused_view) |view| { - if (view.current_tags != tags) { - view.pending_tags = tags; - seat.input_manager.server.root.arrange(); - } + view.pending.tags = tags; + view.output.root.arrange(); } } @@ -75,10 +73,10 @@ pub fn toggleViewTags( ) Error!void { const tags = try parseTags(allocator, args, out); if (seat.focused_view) |view| { - const new_tags = view.current_tags ^ tags; + const new_tags = view.current.tags ^ tags; if (new_tags != 0) { - view.pending_tags = new_tags; - seat.input_manager.server.root.arrange(); + view.pending.tags = new_tags; + view.output.root.arrange(); } } } diff --git a/river/command/toggle_float.zig b/river/command/toggle_float.zig index 4ade683..b918043 100644 --- a/river/command/toggle_float.zig +++ b/river/command/toggle_float.zig @@ -30,9 +30,19 @@ pub fn toggleFloat( ) Error!void { if (args.len > 1) return Error.TooManyArguments; if (seat.focused_view) |view| { - switch (view.mode) { - .layout => view.setMode(.float), - .float => view.setMode(.layout), + switch (view.current.mode) { + .layout => { + view.pending.mode = .float; + view.pending.box = .{ + .x = std.math.max(0, @divTrunc(@intCast(i32, view.output.usable_box.width) - + @intCast(i32, view.natural_width), 2)), + .y = std.math.max(0, @divTrunc(@intCast(i32, view.output.usable_box.height) - + @intCast(i32, view.natural_height), 2)), + .width = view.natural_width, + .height = view.natural_height, + }; + }, + .float => view.pending.mode = .layout, } view.output.root.arrange(); } diff --git a/river/command/zoom.zig b/river/command/zoom.zig index e21e038..783570f 100644 --- a/river/command/zoom.zig +++ b/river/command/zoom.zig @@ -37,7 +37,7 @@ pub fn zoom( const focused_node = @fieldParentPtr(ViewStack(View).Node, "view", current_focus); // Only zoom views that are part of the layout - if (current_focus.mode != .layout) return; + if (current_focus.current.mode != .layout) return; var it = ViewStack(View).iterator(output.views.first, output.current_focused_tags); const zoom_node = if (focused_node == it.next()) diff --git a/river/render.zig b/river/render.zig index 4a2256e..6847ffe 100644 --- a/river/render.zig +++ b/river/render.zig @@ -68,7 +68,7 @@ pub fn renderOutput(output: *Output) void { // This check prevents a race condition when a frame is requested // between mapping of a view and the first configure being handled. - if (view.current_box.width == 0 or view.current_box.height == 0) continue; + if (view.current.box.width == 0 or view.current.box.height == 0) continue; // Focused views are rendered on top of normal views, skip them for now if (view.focused) continue; @@ -84,7 +84,7 @@ pub fn renderOutput(output: *Output) void { // This check prevents a race condition when a frame is requested // between mapping of a view and the first configure being handled. - if (view.current_box.width == 0 or view.current_box.height == 0) continue; + if (view.current.box.width == 0 or view.current.box.height == 0) continue; // Skip unfocused views since we already rendered them if (!view.focused) continue; @@ -144,8 +144,8 @@ fn renderView(output: Output, view: *View, now: *c.timespec) void { output, saved_buffer.wlr_buffer.texture, .{ - .x = saved_buffer.box.x + view.current_box.x - view.saved_surface_box.x, - .y = saved_buffer.box.y + view.current_box.y - view.saved_surface_box.y, + .x = saved_buffer.box.x + view.current.box.x - view.saved_surface_box.x, + .y = saved_buffer.box.y + view.current.box.y - view.saved_surface_box.y, .width = @intCast(c_int, saved_buffer.box.width), .height = @intCast(c_int, saved_buffer.box.height), }, @@ -156,8 +156,8 @@ fn renderView(output: Output, view: *View, now: *c.timespec) void { // a transaction and may simply render each toplevel surface. var rdata = SurfaceRenderData{ .output = &output, - .output_x = view.current_box.x - view.surface_box.x, - .output_y = view.current_box.y - view.surface_box.y, + .output_x = view.current.box.x - view.surface_box.x, + .output_y = view.current.box.y - view.surface_box.y, .when = now, }; @@ -248,29 +248,29 @@ fn renderBorders(output: Output, view: *View, now: *c.timespec) void { const border_width = output.root.server.config.border_width; // left and right, covering the corners as well - border.y = view.current_box.y - @intCast(i32, border_width); + border.y = view.current.box.y - @intCast(i32, border_width); border.width = border_width; - border.height = view.current_box.height + border_width * 2; + border.height = view.current.box.height + border_width * 2; // left - border.x = view.current_box.x - @intCast(i32, border_width); + border.x = view.current.box.x - @intCast(i32, border_width); renderRect(output, border, color); // right - border.x = view.current_box.x + @intCast(i32, view.current_box.width); + border.x = view.current.box.x + @intCast(i32, view.current.box.width); renderRect(output, border, color); // top and bottom - border.x = view.current_box.x; - border.width = view.current_box.width; + border.x = view.current.box.x; + border.width = view.current.box.width; border.height = border_width; // top - border.y = view.current_box.y - @intCast(i32, border_width); + border.y = view.current.box.y - @intCast(i32, border_width); renderRect(output, border, color); // bottom border - border.y = view.current_box.y + @intCast(i32, view.current_box.height); + border.y = view.current.box.y + @intCast(i32, view.current.box.height); renderRect(output, border, color); } diff --git a/river/view_stack.zig b/river/view_stack.zig index 36be14b..2082591 100644 --- a/river/view_stack.zig +++ b/river/view_stack.zig @@ -93,12 +93,9 @@ pub fn ViewStack(comptime T: type) type { pub fn next(self: *Iterator) ?*Node { while (self.it) |node| : (self.it = if (self.reverse) node.prev else node.next) { if (if (self.pending) - if (node.view.pending_tags) |pending_tags| - self.tags & pending_tags != 0 - else - self.tags & node.view.current_tags != 0 + self.tags & node.view.pending.tags != 0 else - self.tags & node.view.current_tags != 0) { + self.tags & node.view.current.tags != 0) { self.it = if (self.reverse) node.prev else node.next; return node; }