render: do basic yes/no damage tracking

This commit is contained in:
Isaac Freund 2021-05-23 17:10:26 +02:00
parent 3390f223a8
commit 13f01bcb4b
11 changed files with 233 additions and 54 deletions

2
deps/zig-pixman vendored

@ -1 +1 @@
Subproject commit 9acac698e073ff54b09a62fecb144de326f67626 Subproject commit 135f22345671e0ae2d1bc4b27cfdee9e97b97dfc

View file

@ -26,6 +26,7 @@ const util = @import("util.zig");
const Box = @import("Box.zig"); const Box = @import("Box.zig");
const Output = @import("Output.zig"); const Output = @import("Output.zig");
const Subsurface = @import("Subsurface.zig");
const XdgPopup = @import("XdgPopup.zig"); const XdgPopup = @import("XdgPopup.zig");
const log = std.log.scoped(.layer_shell); const log = std.log.scoped(.layer_shell);
@ -40,10 +41,11 @@ state: wlr.LayerSurfaceV1.State,
destroy: wl.Listener(*wlr.LayerSurfaceV1) = wl.Listener(*wlr.LayerSurfaceV1).init(handleDestroy), destroy: wl.Listener(*wlr.LayerSurfaceV1) = wl.Listener(*wlr.LayerSurfaceV1).init(handleDestroy),
map: wl.Listener(*wlr.LayerSurfaceV1) = wl.Listener(*wlr.LayerSurfaceV1).init(handleMap), map: wl.Listener(*wlr.LayerSurfaceV1) = wl.Listener(*wlr.LayerSurfaceV1).init(handleMap),
unmap: wl.Listener(*wlr.LayerSurfaceV1) = wl.Listener(*wlr.LayerSurfaceV1).init(handleUnmap), unmap: wl.Listener(*wlr.LayerSurfaceV1) = wl.Listener(*wlr.LayerSurfaceV1).init(handleUnmap),
new_popup: wl.Listener(*wlr.XdgPopup) = wl.Listener(*wlr.XdgPopup).init(handleNewPopup),
new_subsurface: wl.Listener(*wlr.Subsurface) = wl.Listener(*wlr.Subsurface).init(handleNewSubsurface),
// Listeners only active while the layer surface is mapped // Listeners only active while the layer surface is mapped
commit: wl.Listener(*wlr.Surface) = wl.Listener(*wlr.Surface).init(handleCommit), commit: wl.Listener(*wlr.Surface) = wl.Listener(*wlr.Surface).init(handleCommit),
new_popup: wl.Listener(*wlr.XdgPopup) = wl.Listener(*wlr.XdgPopup).init(handleNewPopup),
pub fn init(self: *Self, output: *Output, wlr_layer_surface: *wlr.LayerSurfaceV1) void { pub fn init(self: *Self, output: *Output, wlr_layer_surface: *wlr.LayerSurfaceV1) void {
self.* = .{ self.* = .{
@ -62,9 +64,11 @@ pub fn init(self: *Self, output: *Output, wlr_layer_surface: *wlr.LayerSurfaceV1
list.remove(node); list.remove(node);
// Set up listeners that are active for the entire lifetime of the layer surface // Set up listeners that are active for the entire lifetime of the layer surface
self.wlr_layer_surface.events.destroy.add(&self.destroy); wlr_layer_surface.events.destroy.add(&self.destroy);
self.wlr_layer_surface.events.map.add(&self.map); wlr_layer_surface.events.map.add(&self.map);
self.wlr_layer_surface.events.unmap.add(&self.unmap); wlr_layer_surface.events.unmap.add(&self.unmap);
wlr_layer_surface.events.new_popup.add(&self.new_popup);
wlr_layer_surface.surface.events.new_subsurface.add(&self.new_subsurface);
} }
fn handleDestroy(listener: *wl.Listener(*wlr.LayerSurfaceV1), wlr_layer_surface: *wlr.LayerSurfaceV1) void { fn handleDestroy(listener: *wl.Listener(*wlr.LayerSurfaceV1), wlr_layer_surface: *wlr.LayerSurfaceV1) void {
@ -76,6 +80,8 @@ fn handleDestroy(listener: *wl.Listener(*wlr.LayerSurfaceV1), wlr_layer_surface:
self.destroy.link.remove(); self.destroy.link.remove();
self.map.link.remove(); self.map.link.remove();
self.unmap.link.remove(); self.unmap.link.remove();
self.new_popup.link.remove();
self.new_subsurface.link.remove();
const node = @fieldParentPtr(std.TailQueue(Self).Node, "data", self); const node = @fieldParentPtr(std.TailQueue(Self).Node, "data", self);
util.gpa.destroy(node); util.gpa.destroy(node);
@ -88,7 +94,6 @@ fn handleMap(listener: *wl.Listener(*wlr.LayerSurfaceV1), wlr_layer_surface: *wl
// Add listeners that are only active while mapped // Add listeners that are only active while mapped
wlr_layer_surface.surface.events.commit.add(&self.commit); wlr_layer_surface.surface.events.commit.add(&self.commit);
wlr_layer_surface.events.new_popup.add(&self.new_popup);
wlr_layer_surface.surface.sendEnter(wlr_layer_surface.output.?); wlr_layer_surface.surface.sendEnter(wlr_layer_surface.output.?);
@ -103,7 +108,6 @@ fn handleUnmap(listener: *wl.Listener(*wlr.LayerSurfaceV1), wlr_layer_surface: *
// remove listeners only active while the layer surface is mapped // remove listeners only active while the layer surface is mapped
self.commit.link.remove(); self.commit.link.remove();
self.new_popup.link.remove();
// Remove from the output's list of layer surfaces // Remove from the output's list of layer surfaces
const self_node = @fieldParentPtr(std.TailQueue(Self).Node, "data", self); const self_node = @fieldParentPtr(std.TailQueue(Self).Node, "data", self);
@ -154,15 +158,16 @@ fn handleCommit(listener: *wl.Listener(*wlr.Surface), wlr_surface: *wlr.Surface)
self.output.arrangeLayers(); self.output.arrangeLayers();
server.root.startTransaction(); server.root.startTransaction();
} }
self.output.damage.addWhole();
} }
fn handleNewPopup(listener: *wl.Listener(*wlr.XdgPopup), wlr_xdg_popup: *wlr.XdgPopup) void { fn handleNewPopup(listener: *wl.Listener(*wlr.XdgPopup), wlr_xdg_popup: *wlr.XdgPopup) void {
const self = @fieldParentPtr(Self, "new_popup", listener); const self = @fieldParentPtr(Self, "new_popup", listener);
XdgPopup.create(wlr_xdg_popup, .{ .layer_surface = self });
// This will free itself on destroy }
const xdg_popup = util.gpa.create(XdgPopup) catch {
wlr_xdg_popup.resource.postNoMemory(); fn handleNewSubsurface(listener: *wl.Listener(*wlr.Subsurface), new_wlr_subsurface: *wlr.Subsurface) void {
return; const self = @fieldParentPtr(Self, "new_subsurface", listener);
}; Subsurface.create(new_wlr_subsurface, .{ .layer_surface = self });
xdg_popup.init(self.output, &self.box, wlr_xdg_popup);
} }

View file

@ -55,6 +55,7 @@ const State = struct {
}; };
wlr_output: *wlr.Output, wlr_output: *wlr.Output,
damage: *wlr.OutputDamage,
/// All layer surfaces on the output, indexed by the layer enum. /// All layer surfaces on the output, indexed by the layer enum.
layers: [4]std.TailQueue(LayerSurface) = [1]std.TailQueue(LayerSurface){.{}} ** 4, layers: [4]std.TailQueue(LayerSurface) = [1]std.TailQueue(LayerSurface){.{}} ** 4,
@ -93,8 +94,8 @@ status_trackers: std.SinglyLinkedList(OutputStatus) = .{},
destroy: wl.Listener(*wlr.Output) = wl.Listener(*wlr.Output).init(handleDestroy), destroy: wl.Listener(*wlr.Output) = wl.Listener(*wlr.Output).init(handleDestroy),
enable: wl.Listener(*wlr.Output) = wl.Listener(*wlr.Output).init(handleEnable), enable: wl.Listener(*wlr.Output) = wl.Listener(*wlr.Output).init(handleEnable),
frame: wl.Listener(*wlr.Output) = wl.Listener(*wlr.Output).init(handleFrame),
mode: wl.Listener(*wlr.Output) = wl.Listener(*wlr.Output).init(handleMode), mode: wl.Listener(*wlr.Output) = wl.Listener(*wlr.Output).init(handleMode),
frame: wl.Listener(*wlr.OutputDamage) = wl.Listener(*wlr.OutputDamage).init(handleFrame),
pub fn init(self: *Self, wlr_output: *wlr.Output) !void { pub fn init(self: *Self, wlr_output: *wlr.Output) !void {
// Some backends don't have modes. DRM+KMS does, and we need to set a mode // Some backends don't have modes. DRM+KMS does, and we need to set a mode
@ -110,15 +111,17 @@ pub fn init(self: *Self, wlr_output: *wlr.Output) !void {
self.* = .{ self.* = .{
.wlr_output = wlr_output, .wlr_output = wlr_output,
.damage = try wlr.OutputDamage.create(wlr_output),
.usable_box = undefined, .usable_box = undefined,
}; };
wlr_output.data = @ptrToInt(self); wlr_output.data = @ptrToInt(self);
wlr_output.events.destroy.add(&self.destroy); wlr_output.events.destroy.add(&self.destroy);
wlr_output.events.enable.add(&self.enable); wlr_output.events.enable.add(&self.enable);
wlr_output.events.frame.add(&self.frame);
wlr_output.events.mode.add(&self.mode); wlr_output.events.mode.add(&self.mode);
self.damage.events.frame.add(&self.frame);
if (wlr_output.isNoop()) { if (wlr_output.isNoop()) {
// A noop output is always 0 x 0 // A noop output is always 0 x 0
self.usable_box = .{ self.usable_box = .{
@ -453,7 +456,7 @@ fn handleEnable(listener: *wl.Listener(*wlr.Output), wlr_output: *wlr.Output) vo
if (wlr_output.enabled) server.root.addOutput(self); if (wlr_output.enabled) server.root.addOutput(self);
} }
fn handleFrame(listener: *wl.Listener(*wlr.Output), wlr_output: *wlr.Output) void { fn handleFrame(listener: *wl.Listener(*wlr.OutputDamage), wlr_output: *wlr.OutputDamage) void {
// This function is called every time an output is ready to display a frame, // This function is called every time an output is ready to display a frame,
// generally at the output's refresh rate (e.g. 60Hz). // generally at the output's refresh rate (e.g. 60Hz).
const self = @fieldParentPtr(Self, "frame", listener); const self = @fieldParentPtr(Self, "frame", listener);

View file

@ -404,6 +404,8 @@ fn commitTransaction(self: *Self) void {
} }
if (view_tags_changed) output.sendViewTags(); if (view_tags_changed) output.sendViewTags();
output.damage.addWhole();
} }
} }

103
river/Subsurface.zig Normal file
View file

@ -0,0 +1,103 @@
// This file is part of river, a dynamic tiling wayland compositor.
//
// Copyright 2021 The River Developers
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const Subsurface = @This();
const std = @import("std");
const wlr = @import("wlroots");
const wl = @import("wayland").server.wl;
const util = @import("util.zig");
const LayerSurface = @import("LayerSurface.zig");
const View = @import("View.zig");
pub const Parent = union(enum) {
view: *View,
layer_surface: *LayerSurface,
pub fn damageWholeOutput(parent: Parent) void {
switch (parent) {
.view => |view| view.output.damage.addWhole(),
.layer_surface => |layer_surface| layer_surface.output.damage.addWhole(),
}
}
};
/// The parent at the root of this surface tree
parent: Parent,
wlr_subsurface: *wlr.Subsurface,
// Always active
destroy: wl.Listener(*wlr.Subsurface) = wl.Listener(*wlr.Subsurface).init(handleDestroy),
map: wl.Listener(*wlr.Subsurface) = wl.Listener(*wlr.Subsurface).init(handleMap),
unmap: wl.Listener(*wlr.Subsurface) = wl.Listener(*wlr.Subsurface).init(handleUnmap),
new_subsurface: wl.Listener(*wlr.Subsurface) = wl.Listener(*wlr.Subsurface).init(handleNewSubsurface),
// Only active while mapped
commit: wl.Listener(*wlr.Surface) = wl.Listener(*wlr.Surface).init(handleCommit),
pub fn create(wlr_subsurface: *wlr.Subsurface, parent: Parent) void {
const subsurface = util.gpa.create(Subsurface) catch {
std.log.crit("out of memory", .{});
wlr_subsurface.resource.getClient().postNoMemory();
return;
};
subsurface.* = .{ .wlr_subsurface = wlr_subsurface, .parent = parent };
wlr_subsurface.events.destroy.add(&subsurface.destroy);
wlr_subsurface.events.map.add(&subsurface.map);
wlr_subsurface.events.unmap.add(&subsurface.unmap);
wlr_subsurface.surface.events.new_subsurface.add(&subsurface.new_subsurface);
}
fn handleDestroy(listener: *wl.Listener(*wlr.Subsurface), wlr_subsurface: *wlr.Subsurface) void {
const subsurface = @fieldParentPtr(Subsurface, "destroy", listener);
subsurface.destroy.link.remove();
subsurface.map.link.remove();
subsurface.unmap.link.remove();
subsurface.new_subsurface.link.remove();
util.gpa.destroy(subsurface);
}
fn handleMap(listener: *wl.Listener(*wlr.Subsurface), wlr_subsurface: *wlr.Subsurface) void {
const subsurface = @fieldParentPtr(Subsurface, "map", listener);
wlr_subsurface.surface.events.commit.add(&subsurface.commit);
subsurface.parent.damageWholeOutput();
}
fn handleUnmap(listener: *wl.Listener(*wlr.Subsurface), wlr_subsurface: *wlr.Subsurface) void {
const subsurface = @fieldParentPtr(Subsurface, "unmap", listener);
subsurface.commit.link.remove();
subsurface.parent.damageWholeOutput();
}
fn handleCommit(listener: *wl.Listener(*wlr.Surface), surface: *wlr.Surface) void {
const subsurface = @fieldParentPtr(Subsurface, "commit", listener);
subsurface.parent.damageWholeOutput();
}
fn handleNewSubsurface(listener: *wl.Listener(*wlr.Subsurface), new_wlr_subsurface: *wlr.Subsurface) void {
const subsurface = @fieldParentPtr(Subsurface, "new_subsurface", listener);
Subsurface.create(new_wlr_subsurface, subsurface.parent);
}

View file

@ -278,13 +278,14 @@ pub fn saveBuffers(self: *Self) void {
/// Otherwise, apply the pending state immediately. /// Otherwise, apply the pending state immediately.
pub fn notifyConfiguredOrApplyPending(self: *Self) void { pub fn notifyConfiguredOrApplyPending(self: *Self) void {
self.pending_serial = null; self.pending_serial = null;
if (self.shouldTrackConfigure()) if (self.shouldTrackConfigure()) {
server.root.notifyConfigured() server.root.notifyConfigured();
else { } else {
const self_tags_changed = self.pending.tags != self.current.tags; const self_tags_changed = self.pending.tags != self.current.tags;
self.current = self.pending; self.current = self.pending;
self.commitOpacityTransition(); self.commitOpacityTransition();
if (self_tags_changed) self.output.sendViewTags(); if (self_tags_changed) self.output.sendViewTags();
self.output.damage.addWhole();
} }
} }

View file

@ -23,32 +23,44 @@ const wl = @import("wayland").server.wl;
const util = @import("util.zig"); const util = @import("util.zig");
const Box = @import("Box.zig"); const Subsurface = @import("Subsurface.zig");
const Output = @import("Output.zig"); const Parent = Subsurface.Parent;
const log = std.log.scoped(.server); /// The parent at the root of this surface tree
parent: Parent,
/// The output this popup is displayed on.
output: *Output,
/// Box of the parent of this popup tree. Needed to unconstrain child popups.
parent_box: *const Box,
/// The corresponding wlroots object
wlr_xdg_popup: *wlr.XdgPopup, wlr_xdg_popup: *wlr.XdgPopup,
// Always active
destroy: wl.Listener(*wlr.XdgSurface) = wl.Listener(*wlr.XdgSurface).init(handleDestroy), destroy: wl.Listener(*wlr.XdgSurface) = wl.Listener(*wlr.XdgSurface).init(handleDestroy),
map: wl.Listener(*wlr.XdgSurface) = wl.Listener(*wlr.XdgSurface).init(handleMap),
unmap: wl.Listener(*wlr.XdgSurface) = wl.Listener(*wlr.XdgSurface).init(handleUnmap),
new_popup: wl.Listener(*wlr.XdgPopup) = wl.Listener(*wlr.XdgPopup).init(handleNewPopup), new_popup: wl.Listener(*wlr.XdgPopup) = wl.Listener(*wlr.XdgPopup).init(handleNewPopup),
new_subsurface: wl.Listener(*wlr.Subsurface) = wl.Listener(*wlr.Subsurface).init(handleNewSubsurface),
pub fn init(self: *Self, output: *Output, parent_box: *const Box, wlr_xdg_popup: *wlr.XdgPopup) void { // Only active while mapped
commit: wl.Listener(*wlr.Surface) = wl.Listener(*wlr.Surface).init(handleCommit),
pub fn create(wlr_xdg_popup: *wlr.XdgPopup, parent: Parent) void {
const self = util.gpa.create(Self) catch {
std.log.crit("out of memory", .{});
wlr_xdg_popup.resource.postNoMemory();
return;
};
self.* = .{ self.* = .{
.output = output, .parent = parent,
.parent_box = parent_box,
.wlr_xdg_popup = wlr_xdg_popup, .wlr_xdg_popup = wlr_xdg_popup,
}; };
const parent_box = switch (parent) {
.view => |view| &view.pending.box,
.layer_surface => |layer_surface| &layer_surface.box,
};
const output_dimensions = switch (parent) {
.view => |view| view.output.getEffectiveResolution(),
.layer_surface => |layer_surface| layer_surface.output.getEffectiveResolution(),
};
// The output box relative to the parent of the popup // The output box relative to the parent of the popup
const output_dimensions = output.getEffectiveResolution();
var box = wlr.Box{ var box = wlr.Box{
.x = -parent_box.x, .x = -parent_box.x,
.y = -parent_box.y, .y = -parent_box.y,
@ -58,27 +70,52 @@ pub fn init(self: *Self, output: *Output, parent_box: *const Box, wlr_xdg_popup:
wlr_xdg_popup.unconstrainFromBox(&box); wlr_xdg_popup.unconstrainFromBox(&box);
wlr_xdg_popup.base.events.destroy.add(&self.destroy); wlr_xdg_popup.base.events.destroy.add(&self.destroy);
wlr_xdg_popup.base.events.map.add(&self.map);
wlr_xdg_popup.base.events.unmap.add(&self.unmap);
wlr_xdg_popup.base.events.new_popup.add(&self.new_popup); wlr_xdg_popup.base.events.new_popup.add(&self.new_popup);
wlr_xdg_popup.base.surface.events.new_subsurface.add(&self.new_subsurface);
} }
fn handleDestroy(listener: *wl.Listener(*wlr.XdgSurface), wlr_xdg_surface: *wlr.XdgSurface) void { fn handleDestroy(listener: *wl.Listener(*wlr.XdgSurface), wlr_xdg_surface: *wlr.XdgSurface) void {
const self = @fieldParentPtr(Self, "destroy", listener); const self = @fieldParentPtr(Self, "destroy", listener);
self.destroy.link.remove(); self.destroy.link.remove();
self.map.link.remove();
self.unmap.link.remove();
self.new_popup.link.remove(); self.new_popup.link.remove();
self.new_subsurface.link.remove();
util.gpa.destroy(self); util.gpa.destroy(self);
} }
/// Called when a new xdg popup is requested by the client fn handleMap(listener: *wl.Listener(*wlr.XdgSurface), xdg_surface: *wlr.XdgSurface) void {
const self = @fieldParentPtr(Self, "map", listener);
self.wlr_xdg_popup.base.surface.events.commit.add(&self.commit);
self.parent.damageWholeOutput();
}
fn handleUnmap(listener: *wl.Listener(*wlr.XdgSurface), xdg_surface: *wlr.XdgSurface) void {
const self = @fieldParentPtr(Self, "unmap", listener);
self.commit.link.remove();
self.parent.damageWholeOutput();
}
fn handleCommit(listener: *wl.Listener(*wlr.Surface), surface: *wlr.Surface) void {
const self = @fieldParentPtr(Self, "commit", listener);
self.parent.damageWholeOutput();
}
fn handleNewPopup(listener: *wl.Listener(*wlr.XdgPopup), wlr_xdg_popup: *wlr.XdgPopup) void { fn handleNewPopup(listener: *wl.Listener(*wlr.XdgPopup), wlr_xdg_popup: *wlr.XdgPopup) void {
const self = @fieldParentPtr(Self, "new_popup", listener); const self = @fieldParentPtr(Self, "new_popup", listener);
// This will free itself on destroy Self.create(wlr_xdg_popup, self.parent);
const xdg_popup = util.gpa.create(Self) catch { }
wlr_xdg_popup.resource.postNoMemory();
log.crit("out of memory", .{}); fn handleNewSubsurface(listener: *wl.Listener(*wlr.Subsurface), new_wlr_subsurface: *wlr.Subsurface) void {
return; const self = @fieldParentPtr(Self, "new_subsurface", listener);
};
xdg_popup.init(self.output, self.parent_box, wlr_xdg_popup); Subsurface.create(new_wlr_subsurface, self.parent);
} }

View file

@ -26,6 +26,7 @@ const util = @import("util.zig");
const Box = @import("Box.zig"); const Box = @import("Box.zig");
const Seat = @import("Seat.zig"); const Seat = @import("Seat.zig");
const Subsurface = @import("Subsurface.zig");
const View = @import("View.zig"); const View = @import("View.zig");
const ViewStack = @import("view_stack.zig").ViewStack; const ViewStack = @import("view_stack.zig").ViewStack;
const XdgPopup = @import("XdgPopup.zig"); const XdgPopup = @import("XdgPopup.zig");
@ -42,10 +43,11 @@ xdg_surface: *wlr.XdgSurface,
destroy: wl.Listener(*wlr.XdgSurface) = wl.Listener(*wlr.XdgSurface).init(handleDestroy), destroy: wl.Listener(*wlr.XdgSurface) = wl.Listener(*wlr.XdgSurface).init(handleDestroy),
map: wl.Listener(*wlr.XdgSurface) = wl.Listener(*wlr.XdgSurface).init(handleMap), map: wl.Listener(*wlr.XdgSurface) = wl.Listener(*wlr.XdgSurface).init(handleMap),
unmap: wl.Listener(*wlr.XdgSurface) = wl.Listener(*wlr.XdgSurface).init(handleUnmap), unmap: wl.Listener(*wlr.XdgSurface) = wl.Listener(*wlr.XdgSurface).init(handleUnmap),
new_popup: wl.Listener(*wlr.XdgPopup) = wl.Listener(*wlr.XdgPopup).init(handleNewPopup),
new_subsurface: wl.Listener(*wlr.Subsurface) = wl.Listener(*wlr.Subsurface).init(handleNewSubsurface),
// Listeners that are only active while the view is mapped // Listeners that are only active while the view is mapped
commit: wl.Listener(*wlr.Surface) = wl.Listener(*wlr.Surface).init(handleCommit), commit: wl.Listener(*wlr.Surface) = wl.Listener(*wlr.Surface).init(handleCommit),
new_popup: wl.Listener(*wlr.XdgPopup) = wl.Listener(*wlr.XdgPopup).init(handleNewPopup),
// zig fmt: off // zig fmt: off
request_fullscreen: wl.Listener(*wlr.XdgToplevel.event.SetFullscreen) = request_fullscreen: wl.Listener(*wlr.XdgToplevel.event.SetFullscreen) =
wl.Listener(*wlr.XdgToplevel.event.SetFullscreen).init(handleRequestFullscreen), wl.Listener(*wlr.XdgToplevel.event.SetFullscreen).init(handleRequestFullscreen),
@ -65,6 +67,8 @@ pub fn init(self: *Self, view: *View, xdg_surface: *wlr.XdgSurface) void {
self.xdg_surface.events.destroy.add(&self.destroy); self.xdg_surface.events.destroy.add(&self.destroy);
self.xdg_surface.events.map.add(&self.map); self.xdg_surface.events.map.add(&self.map);
self.xdg_surface.events.unmap.add(&self.unmap); self.xdg_surface.events.unmap.add(&self.unmap);
self.xdg_surface.events.new_popup.add(&self.new_popup);
self.xdg_surface.surface.events.new_subsurface.add(&self.new_subsurface);
} }
pub fn deinit(self: *Self) void { pub fn deinit(self: *Self) void {
@ -73,6 +77,8 @@ pub fn deinit(self: *Self) void {
self.destroy.link.remove(); self.destroy.link.remove();
self.map.link.remove(); self.map.link.remove();
self.unmap.link.remove(); self.unmap.link.remove();
self.new_popup.link.remove();
self.new_subsurface.link.remove();
} }
} }
@ -161,7 +167,6 @@ fn handleMap(listener: *wl.Listener(*wlr.XdgSurface), xdg_surface: *wlr.XdgSurfa
// Add listeners that are only active while mapped // Add listeners that are only active while mapped
self.xdg_surface.surface.events.commit.add(&self.commit); self.xdg_surface.surface.events.commit.add(&self.commit);
self.xdg_surface.events.new_popup.add(&self.new_popup);
toplevel.events.request_fullscreen.add(&self.request_fullscreen); toplevel.events.request_fullscreen.add(&self.request_fullscreen);
toplevel.events.request_move.add(&self.request_move); toplevel.events.request_move.add(&self.request_move);
toplevel.events.request_resize.add(&self.request_resize); toplevel.events.request_resize.add(&self.request_resize);
@ -229,7 +234,6 @@ fn handleUnmap(listener: *wl.Listener(*wlr.XdgSurface), xdg_surface: *wlr.XdgSur
// Remove listeners that are only active while mapped // Remove listeners that are only active while mapped
self.commit.link.remove(); self.commit.link.remove();
self.new_popup.link.remove();
self.request_fullscreen.link.remove(); self.request_fullscreen.link.remove();
self.request_move.link.remove(); self.request_move.link.remove();
self.request_resize.link.remove(); self.request_resize.link.remove();
@ -260,6 +264,7 @@ fn handleCommit(listener: *wl.Listener(*wlr.Surface), surface: *wlr.Surface) voi
view.sendFrameDone(); view.sendFrameDone();
} }
} else { } else {
view.output.damage.addWhole();
// TODO: handle unexpected change in dimensions // TODO: handle unexpected change in dimensions
if (!std.meta.eql(view.surface_box, new_box)) if (!std.meta.eql(view.surface_box, new_box))
log.err("view changed size unexpectedly", .{}); log.err("view changed size unexpectedly", .{});
@ -267,16 +272,14 @@ fn handleCommit(listener: *wl.Listener(*wlr.Surface), surface: *wlr.Surface) voi
} }
} }
/// Called when a new xdg popup is requested by the client
fn handleNewPopup(listener: *wl.Listener(*wlr.XdgPopup), wlr_xdg_popup: *wlr.XdgPopup) void { fn handleNewPopup(listener: *wl.Listener(*wlr.XdgPopup), wlr_xdg_popup: *wlr.XdgPopup) void {
const self = @fieldParentPtr(Self, "new_popup", listener); const self = @fieldParentPtr(Self, "new_popup", listener);
XdgPopup.create(wlr_xdg_popup, .{ .view = self.view });
}
// This will free itself on destroy fn handleNewSubsurface(listener: *wl.Listener(*wlr.Subsurface), new_wlr_subsurface: *wlr.Subsurface) void {
const xdg_popup = util.gpa.create(XdgPopup) catch { const self = @fieldParentPtr(Self, "new_subsurface", listener);
wlr_xdg_popup.resource.postNoMemory(); Subsurface.create(new_wlr_subsurface, .{ .view = self.view });
return;
};
xdg_popup.init(self.view.output, &self.view.current.box, wlr_xdg_popup);
} }
/// Called when the client asks to be fullscreened. We always honor the request /// Called when the client asks to be fullscreened. We always honor the request

View file

@ -37,6 +37,7 @@ request_configure: wl.Listener(*wlr.XwaylandSurface.event.Configure) =
destroy: wl.Listener(*wlr.XwaylandSurface) = wl.Listener(*wlr.XwaylandSurface).init(handleDestroy), destroy: wl.Listener(*wlr.XwaylandSurface) = wl.Listener(*wlr.XwaylandSurface).init(handleDestroy),
map: wl.Listener(*wlr.XwaylandSurface) = wl.Listener(*wlr.XwaylandSurface).init(handleMap), map: wl.Listener(*wlr.XwaylandSurface) = wl.Listener(*wlr.XwaylandSurface).init(handleMap),
unmap: wl.Listener(*wlr.XwaylandSurface) = wl.Listener(*wlr.XwaylandSurface).init(handleUnmap), unmap: wl.Listener(*wlr.XwaylandSurface) = wl.Listener(*wlr.XwaylandSurface).init(handleUnmap),
commit: wl.Listener(*wlr.Surface) = wl.Listener(*wlr.Surface).init(handleCommit),
pub fn init(self: *Self, xwayland_surface: *wlr.XwaylandSurface) void { pub fn init(self: *Self, xwayland_surface: *wlr.XwaylandSurface) void {
self.* = .{ .xwayland_surface = xwayland_surface }; self.* = .{ .xwayland_surface = xwayland_surface };
@ -78,6 +79,8 @@ fn handleMap(listener: *wl.Listener(*wlr.XwaylandSurface), xwayland_surface: *wl
const node = @fieldParentPtr(std.TailQueue(Self).Node, "data", self); const node = @fieldParentPtr(std.TailQueue(Self).Node, "data", self);
server.root.xwayland_unmanaged_views.prepend(node); server.root.xwayland_unmanaged_views.prepend(node);
xwayland_surface.surface.?.events.commit.add(&self.commit);
// TODO: handle keyboard focus // TODO: handle keyboard focus
// if (wlr_xwayland_or_surface_wants_focus(self.xwayland_surface)) { ... // if (wlr_xwayland_or_surface_wants_focus(self.xwayland_surface)) { ...
} }
@ -89,4 +92,11 @@ fn handleUnmap(listener: *wl.Listener(*wlr.XwaylandSurface), xwayland_surface: *
// Remove self from the list of unmanged views in the root // Remove self from the list of unmanged views in the root
const node = @fieldParentPtr(std.TailQueue(Self).Node, "data", self); const node = @fieldParentPtr(std.TailQueue(Self).Node, "data", self);
server.root.xwayland_unmanaged_views.remove(node); server.root.xwayland_unmanaged_views.remove(node);
self.commit.link.remove();
}
fn handleCommit(listener: *wl.Listener(*wlr.Surface), surface: *wlr.Surface) void {
var it = server.root.outputs.first;
while (it) |node| : (it = node.next) node.data.damage.addWhole();
} }

View file

@ -232,6 +232,9 @@ fn handleRequestConfigure(
/// TODO: check for unexpected change in size and react as needed /// TODO: check for unexpected change in size and react as needed
fn handleCommit(listener: *wl.Listener(*wlr.Surface), surface: *wlr.Surface) void { fn handleCommit(listener: *wl.Listener(*wlr.Surface), surface: *wlr.Surface) void {
const self = @fieldParentPtr(Self, "commit", listener); const self = @fieldParentPtr(Self, "commit", listener);
self.view.output.damage.addWhole();
self.view.surface_box = Box{ self.view.surface_box = Box{
.x = 0, .x = 0,
.y = 0, .y = 0,

View file

@ -53,7 +53,19 @@ pub fn renderOutput(output: *Output) void {
var now: os.timespec = undefined; var now: os.timespec = undefined;
os.clock_gettime(os.CLOCK_MONOTONIC, &now) catch unreachable; os.clock_gettime(os.CLOCK_MONOTONIC, &now) catch unreachable;
output.wlr_output.attachRender(null) catch return; var needs_frame: bool = undefined;
var damage_region: pixman.Region32 = undefined;
damage_region.init();
defer damage_region.deinit();
output.damage.attachRender(&needs_frame, &damage_region) catch {
log.err("failed to attach renderer", .{});
return;
};
if (!needs_frame) {
output.wlr_output.rollback();
return;
}
renderer.begin(@intCast(u32, output.wlr_output.width), @intCast(u32, output.wlr_output.height)); renderer.begin(@intCast(u32, output.wlr_output.width), @intCast(u32, output.wlr_output.height));