view: send activated/fullscreen configures immediately
The transaction system exists to coordinate size changes of all views in a layout in order to achieve frame perfection. Since many clients do not need to commit a new buffer in response to a activated state change alone, this breaks things when such a configure event is tracked by the transaction system. Instead, simply send activated and fullscreen configures right away but still track this state in a double-buffered way so that e.g. border color changes based on focus are frame-perfect. This also fixes a related issue with the transaction system where views that did not need to commit in response to our first configure were not rendered until their next frame.
This commit is contained in:
parent
021fd8f376
commit
e90474657f
5 changed files with 72 additions and 27 deletions
|
@ -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 => {},
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue