xdg-shell: improve child handling

- wait until map to send tiled state
- only set toplevels with no parent to tiled (toplevels with a parent
are generally popup-like things such as a file chooser or yes/no prompt)
- track dimesions and offset of the surface and take offset into account
for rendering.
This commit is contained in:
Isaac Freund 2020-06-13 14:42:31 +02:00
parent c2d32a44c3
commit e1a1459177
No known key found for this signature in database
GPG Key ID: 86DED400DDFD7A11
5 changed files with 71 additions and 21 deletions

View File

@ -24,6 +24,15 @@ y: i32,
width: u32,
height: u32,
pub fn fromWlrBox(wlr_box: c.wlr_box) Self {
return Self{
.x = @intCast(i32, wlr_box.x),
.y = @intCast(i32, wlr_box.y),
.width = @intCast(u32, wlr_box.width),
.height = @intCast(u32, wlr_box.height),
};
}
pub fn toWlrBox(self: Self) c.wlr_box {
return c.wlr_box{
.x = @intCast(c_int, self.x),

View File

@ -59,10 +59,25 @@ floating: bool,
/// 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 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,
/// Pending dimensions of the view during a transaction
pending_box: ?Box,
/// 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.
surface_box: Box,
/// The geometry the view's surface had when the transaction started and
/// buffers were saved.
saved_surface_box: Box,
/// These are what we render while a transaction is in progress
saved_buffers: std.ArrayList(SavedBuffer),
/// The dimensions the view would have taken if we didn't force it to tile
natural_width: u32,
natural_height: u32,
@ -72,9 +87,6 @@ pending_tags: ?u32,
pending_serial: ?u32,
/// These are what we render while a transaction is in progress
saved_buffers: std.ArrayList(SavedBuffer),
pub fn init(
self: *Self,
output: *Output,
@ -151,6 +163,7 @@ pub fn saveBuffers(self: *Self) void {
self.saved_buffers.items.len = 0;
}
self.saved_surface_box = self.surface_box;
self.forEachSurface(saveBuffersIterator, &self.saved_buffers);
}

View File

@ -47,11 +47,6 @@ pub fn init(self: *Self, view: *View, wlr_xdg_surface: *c.wlr_xdg_surface) void
self.wlr_xdg_surface = wlr_xdg_surface;
wlr_xdg_surface.data = self;
// Inform the xdg toplevel that it is tiled.
// For example this prevents firefox from drawing shadows around itself
_ = c.wlr_xdg_toplevel_set_tiled(self.wlr_xdg_surface, c.WLR_EDGE_LEFT |
c.WLR_EDGE_RIGHT | c.WLR_EDGE_TOP | c.WLR_EDGE_BOTTOM);
// Add listeners that are active over the view's entire lifetime
self.listen_destroy.notify = handleDestroy;
c.wl_signal_add(&self.wlr_xdg_surface.events.destroy, &self.listen_destroy);
@ -110,10 +105,11 @@ pub fn forEachSurface(
/// 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) ?*c.wlr_surface {
const view = self.view;
return c.wlr_xdg_surface_surface_at(
self.wlr_xdg_surface,
ox - @intToFloat(f64, self.view.current_box.x),
oy - @intToFloat(f64, self.view.current_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,
);
@ -186,6 +182,14 @@ fn handleMap(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
view.setFloating(true);
}
// If the toplevel has no parent, inform it that it is tiled. This
// prevents firefox, for example, from drawing shadows around itself.
if (wlr_xdg_toplevel.parent == null)
_ = c.wlr_xdg_toplevel_set_tiled(
self.wlr_xdg_surface,
c.WLR_EDGE_LEFT | c.WLR_EDGE_RIGHT | c.WLR_EDGE_TOP | c.WLR_EDGE_BOTTOM,
);
view.map();
}
@ -202,22 +206,36 @@ fn handleUnmap(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
}
/// Called when the surface is comitted
/// TODO: check for unexpected change in size and react as needed
fn handleCommit(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
const self = @fieldParentPtr(Self, "listen_commit", listener.?);
const view = self.view;
var wlr_box: c.wlr_box = undefined;
c.wlr_xdg_surface_get_geometry(self.wlr_xdg_surface, &wlr_box);
const new_box = Box.fromWlrBox(wlr_box);
// If we have sent a configure changing the size
if (view.pending_serial) |s| {
// Update the stored dimensions of the surface
view.surface_box = new_box;
if (s == self.wlr_xdg_surface.configure_serial) {
// If this commit is in response to our configure, notify the
// transaction code.
view.output.root.notifyConfigured();
view.pending_serial = null;
} else {
// If the view has not yet acked our configure, we need to send a
// frame done event so that they commit another buffer. These
// If the client has not yet acked our configure, we need to send a
// frame done event so that it commits another buffer. These
// buffers won't be rendered since we are still rendering our
// stashed buffer from when the transaction started.
view.sendFrameDone();
}
} else {
// TODO: handle unexpected change in dimensions
if (!std.meta.eql(view.surface_box, new_box))
Log.Error.log("View changed size unexpectedly", .{});
view.surface_box = new_box;
}
}

View File

@ -57,10 +57,12 @@ pub fn init(self: *Self, view: *View, wlr_xwayland_surface: *c.wlr_xwayland_surf
c.wl_signal_add(&self.wlr_xwayland_surface.events.unmap, &self.listen_unmap);
}
/// Don't really care about efficiency with xwayland, we don't wait for them
/// to ack anyways since they don't use serials.
pub fn needsConfigure(self: Self) bool {
return true;
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;
}
/// Tell the client to take a new size
@ -162,6 +164,14 @@ fn handleUnmap(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
fn handleCommit(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
const self = @fieldParentPtr(Self, "listen_commit", listener.?);
const view = self.view;
view.surface_box = Box{
.x = 0,
.y = 0,
.width = @intCast(u32, self.wlr_xwayland_surface.surface.*.current.width),
.height = @intCast(u32, self.wlr_xwayland_surface.surface.*.current.height),
};
// See comment in XwaylandView.configure()
if (view.pending_serial != null) {
view.output.root.notifyConfigured();

View File

@ -143,8 +143,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,
.y = saved_buffer.box.y + view.current_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),
},
@ -155,8 +155,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,
.output_y = view.current_box.y,
.output_x = view.current_box.x - view.surface_box.x,
.output_y = view.current_box.y - view.surface_box.y,
.when = now,
};