diff --git a/river/Seat.zig b/river/Seat.zig index d71ac1a..b91a309 100644 --- a/river/Seat.zig +++ b/river/Seat.zig @@ -196,29 +196,30 @@ pub fn setFocusRaw(self: *Self, new_focus: FocusTarget) void { // still clear the focus. if (if (target_surface) |wlr_surface| server.input_manager.inputAllowed(wlr_surface) else true) { // First clear the current focus - if (self.focused == .view) { - self.focused.view.pending.focus -= 1; - // This is needed because xwayland views don't double buffer - // activated state. - if (build_options.xwayland and self.focused.view.impl == .xwayland_view) - self.focused.view.impl.xwayland_view.xwayland_surface.activate(false); - if (self.focused.view.pending.focus == 0 and !self.focused.view.pending.fullscreen) { - self.focused.view.pending.target_opacity = server.config.opacity.unfocused; - } + switch (self.focused) { + .view => |view| { + view.pending.focus -= 1; + if (view.pending.focus == 0) { + view.setActivated(false); + if (!view.pending.fullscreen) { + view.pending.target_opacity = server.config.opacity.unfocused; + } + } + }, + .layer, .none => {}, } // Set the new focus switch (new_focus) { .view => |target_view| { std.debug.assert(self.focused_output == target_view.output); - target_view.pending.focus += 1; - // This is needed because xwayland views don't double buffer - // activated state. - if (build_options.xwayland and target_view.impl == .xwayland_view) - target_view.impl.xwayland_view.xwayland_surface.activate(true); - if (!target_view.pending.fullscreen) { - target_view.pending.target_opacity = server.config.opacity.focused; + if (target_view.pending.focus == 0) { + target_view.setActivated(true); + if (!target_view.pending.fullscreen) { + target_view.pending.target_opacity = server.config.opacity.focused; + } } + target_view.pending.focus += 1; }, .layer => |target_layer| std.debug.assert(self.focused_output == target_layer.output), .none => {}, diff --git a/river/View.zig b/river/View.zig index 71707ba..a8b9c63 100644 --- a/river/View.zig +++ b/river/View.zig @@ -209,6 +209,7 @@ pub fn applyPending(self: *Self) void { // If switching to fullscreen set the dimensions to the full area of the output // and turn the view fully opaque if (!self.current.fullscreen and self.pending.fullscreen) { + self.setFullscreen(true); self.post_fullscreen_box = self.current.box; self.pending.target_opacity = 1.0; @@ -222,6 +223,7 @@ pub fn applyPending(self: *Self) void { } if (self.current.fullscreen and !self.pending.fullscreen) { + self.setFullscreen(false); self.pending.box = self.post_fullscreen_box; // Restore configured opacity @@ -244,10 +246,6 @@ pub fn needsConfigure(self: Self) bool { } pub fn configure(self: Self) void { - if (self.foreign_toplevel_handle) |handle| { - handle.setActivated(self.pending.focus != 0); - handle.setFullscreen(self.pending.fullscreen); - } switch (self.impl) { .xdg_toplevel => |xdg_toplevel| xdg_toplevel.configure(), .xwayland_view => |xwayland_view| xwayland_view.configure(), @@ -322,6 +320,23 @@ pub fn close(self: Self) void { .xwayland_view => |xwayland_view| xwayland_view.close(), } } + +pub fn setActivated(self: Self, activated: bool) void { + if (self.foreign_toplevel_handle) |handle| handle.setActivated(activated); + switch (self.impl) { + .xdg_toplevel => |xdg_toplevel| xdg_toplevel.setActivated(activated), + .xwayland_view => |xwayland_view| xwayland_view.setActivated(activated), + } +} + +pub fn setFullscreen(self: Self, fullscreen: bool) void { + if (self.foreign_toplevel_handle) |handle| handle.setFullscreen(fullscreen); + switch (self.impl) { + .xdg_toplevel => |xdg_toplevel| xdg_toplevel.setFullscreen(fullscreen), + .xwayland_view => |xwayland_view| xwayland_view.setFullscreen(fullscreen), + } +} + pub inline fn forEachPopupSurface( self: Self, comptime T: type, diff --git a/river/VoidView.zig b/river/VoidView.zig index 4fe4d7d..e0d6edd 100644 --- a/river/VoidView.zig +++ b/river/VoidView.zig @@ -39,6 +39,14 @@ pub fn close(self: Self) void { unreachable; } +pub fn setActivated(self: Self, activated: bool) void { + unreachable; +} + +pub fn setFullscreen(self: Self, fullscreen: bool) void { + unreachable; +} + pub fn surfaceAt(self: Self, ox: f64, oy: f64, sx: *f64, sy: *f64) ?*wlr.Surface { unreachable; } diff --git a/river/XdgToplevel.zig b/river/XdgToplevel.zig index 3870fa7..91664d8 100644 --- a/river/XdgToplevel.zig +++ b/river/XdgToplevel.zig @@ -85,8 +85,8 @@ pub fn deinit(self: *Self) void { } } -/// Returns true if a configure must be sent to ensure the dimensions of the -/// pending_box are applied. +/// Returns true if a configure must be sent to ensure that the pending +/// dimensions are applied. pub fn needsConfigure(self: Self) bool { const server_pending = &self.xdg_surface.role_data.toplevel.server_pending; const state = &self.view.pending; @@ -95,8 +95,10 @@ 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 (state.focus != 0) != server_pending.activated or - state.box.width != server_pending.width or + // Furthermore, we avoid a special case for newly mapped views which we + // have not yet configured by setting server_pending.width/height to the + // initial width/height of the view in handleMap(). + return state.box.width != server_pending.width or state.box.height != server_pending.height; } @@ -104,8 +106,6 @@ pub fn needsConfigure(self: Self) bool { pub fn configure(self: Self) void { const toplevel = self.xdg_surface.role_data.toplevel; const state = &self.view.pending; - _ = toplevel.setActivated(state.focus != 0); - _ = toplevel.setFullscreen(state.fullscreen); self.view.pending_serial = toplevel.setSize(state.box.width, state.box.height); } @@ -113,6 +113,15 @@ pub fn configure(self: Self) void { pub fn close(self: Self) void { self.xdg_surface.role_data.toplevel.sendClose(); } + +pub fn setActivated(self: Self, activated: bool) void { + _ = self.xdg_surface.role_data.toplevel.setActivated(activated); +} + +pub fn setFullscreen(self: Self, fullscreen: bool) void { + _ = self.xdg_surface.role_data.toplevel.setFullscreen(fullscreen); +} + pub inline fn forEachPopupSurface( self: Self, comptime T: type, @@ -189,6 +198,11 @@ fn handleMap(listener: *wl.Listener(*wlr.XdgSurface), xdg_surface: *wlr.XdgSurfa view.float_box.y = std.math.max(0, @divTrunc(@intCast(i32, view.output.usable_box.height) - @intCast(i32, view.float_box.height), 2)); + // We initialize these to avoid special-casing newly mapped views in + // the check preformed in needsConfigure(). + toplevel.server_pending.width = @intCast(u32, initial_box.width); + toplevel.server_pending.height = @intCast(u32, initial_box.height); + // Also use the view's "natural" size as the initial regular dimensions, // for the case that it does not get arranged by a lyaout. view.pending.box = view.float_box; diff --git a/river/XwaylandView.zig b/river/XwaylandView.zig index 4f9cfab..c474be6 100644 --- a/river/XwaylandView.zig +++ b/river/XwaylandView.zig @@ -84,7 +84,6 @@ pub fn configure(self: Self) void { const output_box = server.root.output_layout.getBox(output.wlr_output).?; const state = &self.view.pending; - self.xwayland_surface.setFullscreen(state.fullscreen); self.xwayland_surface.configure( @intCast(i16, state.box.x + output_box.x), @intCast(i16, state.box.y + output_box.y), @@ -98,6 +97,14 @@ pub fn close(self: Self) void { self.xwayland_surface.close(); } +pub fn setActivated(self: Self, activated: bool) void { + self.xwayland_surface.activate(activated); +} + +pub fn setFullscreen(self: Self, fullscreen: bool) void { + self.xwayland_surface.setFullscreen(fullscreen); +} + /// Return the surface at output coordinates ox, oy and set sx, sy to the /// corresponding surface-relative coordinates, if there is a surface. pub fn surfaceAt(self: Self, ox: f64, oy: f64, sx: *f64, sy: *f64) ?*wlr.Surface {