render: do basic yes/no damage tracking
This commit is contained in:
		
							
								
								
									
										2
									
								
								deps/zig-pixman
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
							
						
						
									
										2
									
								
								deps/zig-pixman
									
									
									
									
										vendored
									
									
								
							 Submodule deps/zig-pixman updated: 9acac698e0...135f223456
									
								
							| @ -26,6 +26,7 @@ const util = @import("util.zig"); | ||||
|  | ||||
| const Box = @import("Box.zig"); | ||||
| const Output = @import("Output.zig"); | ||||
| const Subsurface = @import("Subsurface.zig"); | ||||
| const XdgPopup = @import("XdgPopup.zig"); | ||||
|  | ||||
| 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), | ||||
| map: wl.Listener(*wlr.LayerSurfaceV1) = wl.Listener(*wlr.LayerSurfaceV1).init(handleMap), | ||||
| 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 | ||||
| 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 { | ||||
|     self.* = .{ | ||||
| @ -62,9 +64,11 @@ pub fn init(self: *Self, output: *Output, wlr_layer_surface: *wlr.LayerSurfaceV1 | ||||
|     list.remove(node); | ||||
|  | ||||
|     // Set up listeners that are active for the entire lifetime of the layer surface | ||||
|     self.wlr_layer_surface.events.destroy.add(&self.destroy); | ||||
|     self.wlr_layer_surface.events.map.add(&self.map); | ||||
|     self.wlr_layer_surface.events.unmap.add(&self.unmap); | ||||
|     wlr_layer_surface.events.destroy.add(&self.destroy); | ||||
|     wlr_layer_surface.events.map.add(&self.map); | ||||
|     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 { | ||||
| @ -76,6 +80,8 @@ fn handleDestroy(listener: *wl.Listener(*wlr.LayerSurfaceV1), wlr_layer_surface: | ||||
|     self.destroy.link.remove(); | ||||
|     self.map.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); | ||||
|     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 | ||||
|     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.?); | ||||
|  | ||||
| @ -103,7 +108,6 @@ fn handleUnmap(listener: *wl.Listener(*wlr.LayerSurfaceV1), wlr_layer_surface: * | ||||
|  | ||||
|     // remove listeners only active while the layer surface is mapped | ||||
|     self.commit.link.remove(); | ||||
|     self.new_popup.link.remove(); | ||||
|  | ||||
|     // Remove from the output's list of layer surfaces | ||||
|     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(); | ||||
|         server.root.startTransaction(); | ||||
|     } | ||||
|  | ||||
|     self.output.damage.addWhole(); | ||||
| } | ||||
|  | ||||
| fn handleNewPopup(listener: *wl.Listener(*wlr.XdgPopup), wlr_xdg_popup: *wlr.XdgPopup) void { | ||||
|     const self = @fieldParentPtr(Self, "new_popup", listener); | ||||
|  | ||||
|     // This will free itself on destroy | ||||
|     const xdg_popup = util.gpa.create(XdgPopup) catch { | ||||
|         wlr_xdg_popup.resource.postNoMemory(); | ||||
|         return; | ||||
|     }; | ||||
|     xdg_popup.init(self.output, &self.box, wlr_xdg_popup); | ||||
|     XdgPopup.create(wlr_xdg_popup, .{ .layer_surface = self }); | ||||
| } | ||||
|  | ||||
| fn handleNewSubsurface(listener: *wl.Listener(*wlr.Subsurface), new_wlr_subsurface: *wlr.Subsurface) void { | ||||
|     const self = @fieldParentPtr(Self, "new_subsurface", listener); | ||||
|     Subsurface.create(new_wlr_subsurface, .{ .layer_surface = self }); | ||||
| } | ||||
|  | ||||
| @ -55,6 +55,7 @@ const State = struct { | ||||
| }; | ||||
|  | ||||
| wlr_output: *wlr.Output, | ||||
| damage: *wlr.OutputDamage, | ||||
|  | ||||
| /// All layer surfaces on the output, indexed by the layer enum. | ||||
| 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), | ||||
| 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), | ||||
| frame: wl.Listener(*wlr.OutputDamage) = wl.Listener(*wlr.OutputDamage).init(handleFrame), | ||||
|  | ||||
| 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 | ||||
| @ -110,15 +111,17 @@ pub fn init(self: *Self, wlr_output: *wlr.Output) !void { | ||||
|  | ||||
|     self.* = .{ | ||||
|         .wlr_output = wlr_output, | ||||
|         .damage = try wlr.OutputDamage.create(wlr_output), | ||||
|         .usable_box = undefined, | ||||
|     }; | ||||
|     wlr_output.data = @ptrToInt(self); | ||||
|  | ||||
|     wlr_output.events.destroy.add(&self.destroy); | ||||
|     wlr_output.events.enable.add(&self.enable); | ||||
|     wlr_output.events.frame.add(&self.frame); | ||||
|     wlr_output.events.mode.add(&self.mode); | ||||
|  | ||||
|     self.damage.events.frame.add(&self.frame); | ||||
|  | ||||
|     if (wlr_output.isNoop()) { | ||||
|         // A noop output is always 0 x 0 | ||||
|         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); | ||||
| } | ||||
|  | ||||
| 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, | ||||
|     // generally at the output's refresh rate (e.g. 60Hz). | ||||
|     const self = @fieldParentPtr(Self, "frame", listener); | ||||
|  | ||||
| @ -404,6 +404,8 @@ fn commitTransaction(self: *Self) void { | ||||
|         } | ||||
|  | ||||
|         if (view_tags_changed) output.sendViewTags(); | ||||
|  | ||||
|         output.damage.addWhole(); | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
							
								
								
									
										103
									
								
								river/Subsurface.zig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								river/Subsurface.zig
									
									
									
									
									
										Normal 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); | ||||
| } | ||||
| @ -278,13 +278,14 @@ pub fn saveBuffers(self: *Self) void { | ||||
| /// Otherwise, apply the pending state immediately. | ||||
| pub fn notifyConfiguredOrApplyPending(self: *Self) void { | ||||
|     self.pending_serial = null; | ||||
|     if (self.shouldTrackConfigure()) | ||||
|         server.root.notifyConfigured() | ||||
|     else { | ||||
|     if (self.shouldTrackConfigure()) { | ||||
|         server.root.notifyConfigured(); | ||||
|     } else { | ||||
|         const self_tags_changed = self.pending.tags != self.current.tags; | ||||
|         self.current = self.pending; | ||||
|         self.commitOpacityTransition(); | ||||
|         if (self_tags_changed) self.output.sendViewTags(); | ||||
|         self.output.damage.addWhole(); | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| @ -23,32 +23,44 @@ const wl = @import("wayland").server.wl; | ||||
|  | ||||
| const util = @import("util.zig"); | ||||
|  | ||||
| const Box = @import("Box.zig"); | ||||
| const Output = @import("Output.zig"); | ||||
| const Subsurface = @import("Subsurface.zig"); | ||||
| const Parent = Subsurface.Parent; | ||||
|  | ||||
| const log = std.log.scoped(.server); | ||||
|  | ||||
| /// 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 | ||||
| /// The parent at the root of this surface tree | ||||
| parent: Parent, | ||||
| wlr_xdg_popup: *wlr.XdgPopup, | ||||
|  | ||||
| // Always active | ||||
| 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_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.* = .{ | ||||
|         .output = output, | ||||
|         .parent_box = parent_box, | ||||
|         .parent = parent, | ||||
|         .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 | ||||
|     const output_dimensions = output.getEffectiveResolution(); | ||||
|     var box = wlr.Box{ | ||||
|         .x = -parent_box.x, | ||||
|         .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.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.surface.events.new_subsurface.add(&self.new_subsurface); | ||||
| } | ||||
|  | ||||
| fn handleDestroy(listener: *wl.Listener(*wlr.XdgSurface), wlr_xdg_surface: *wlr.XdgSurface) void { | ||||
|     const self = @fieldParentPtr(Self, "destroy", listener); | ||||
|  | ||||
|     self.destroy.link.remove(); | ||||
|     self.map.link.remove(); | ||||
|     self.unmap.link.remove(); | ||||
|     self.new_popup.link.remove(); | ||||
|     self.new_subsurface.link.remove(); | ||||
|  | ||||
|     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 { | ||||
|     const self = @fieldParentPtr(Self, "new_popup", listener); | ||||
|  | ||||
|     // This will free itself on destroy | ||||
|     const xdg_popup = util.gpa.create(Self) catch { | ||||
|         wlr_xdg_popup.resource.postNoMemory(); | ||||
|         log.crit("out of memory", .{}); | ||||
|         return; | ||||
|     }; | ||||
|     xdg_popup.init(self.output, self.parent_box, wlr_xdg_popup); | ||||
|     Self.create(wlr_xdg_popup, self.parent); | ||||
| } | ||||
|  | ||||
| fn handleNewSubsurface(listener: *wl.Listener(*wlr.Subsurface), new_wlr_subsurface: *wlr.Subsurface) void { | ||||
|     const self = @fieldParentPtr(Self, "new_subsurface", listener); | ||||
|  | ||||
|     Subsurface.create(new_wlr_subsurface, self.parent); | ||||
| } | ||||
|  | ||||
| @ -26,6 +26,7 @@ const util = @import("util.zig"); | ||||
|  | ||||
| const Box = @import("Box.zig"); | ||||
| const Seat = @import("Seat.zig"); | ||||
| const Subsurface = @import("Subsurface.zig"); | ||||
| const View = @import("View.zig"); | ||||
| const ViewStack = @import("view_stack.zig").ViewStack; | ||||
| const XdgPopup = @import("XdgPopup.zig"); | ||||
| @ -42,10 +43,11 @@ xdg_surface: *wlr.XdgSurface, | ||||
| 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_subsurface: wl.Listener(*wlr.Subsurface) = wl.Listener(*wlr.Subsurface).init(handleNewSubsurface), | ||||
|  | ||||
| // Listeners that are only active while the view is mapped | ||||
| 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 | ||||
| request_fullscreen: wl.Listener(*wlr.XdgToplevel.event.SetFullscreen) = | ||||
|     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.map.add(&self.map); | ||||
|     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 { | ||||
| @ -73,6 +77,8 @@ pub fn deinit(self: *Self) void { | ||||
|         self.destroy.link.remove(); | ||||
|         self.map.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 | ||||
|     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_move.add(&self.request_move); | ||||
|     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 | ||||
|     self.commit.link.remove(); | ||||
|     self.new_popup.link.remove(); | ||||
|     self.request_fullscreen.link.remove(); | ||||
|     self.request_move.link.remove(); | ||||
|     self.request_resize.link.remove(); | ||||
| @ -260,6 +264,7 @@ fn handleCommit(listener: *wl.Listener(*wlr.Surface), surface: *wlr.Surface) voi | ||||
|             view.sendFrameDone(); | ||||
|         } | ||||
|     } else { | ||||
|         view.output.damage.addWhole(); | ||||
|         // TODO: handle unexpected change in dimensions | ||||
|         if (!std.meta.eql(view.surface_box, new_box)) | ||||
|             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 { | ||||
|     const self = @fieldParentPtr(Self, "new_popup", listener); | ||||
|     XdgPopup.create(wlr_xdg_popup, .{ .view = self.view }); | ||||
| } | ||||
|  | ||||
|     // This will free itself on destroy | ||||
|     const xdg_popup = util.gpa.create(XdgPopup) catch { | ||||
|         wlr_xdg_popup.resource.postNoMemory(); | ||||
|         return; | ||||
|     }; | ||||
|     xdg_popup.init(self.view.output, &self.view.current.box, wlr_xdg_popup); | ||||
| fn handleNewSubsurface(listener: *wl.Listener(*wlr.Subsurface), new_wlr_subsurface: *wlr.Subsurface) void { | ||||
|     const self = @fieldParentPtr(Self, "new_subsurface", listener); | ||||
|     Subsurface.create(new_wlr_subsurface, .{ .view = self.view }); | ||||
| } | ||||
|  | ||||
| /// Called when the client asks to be fullscreened. We always honor the request | ||||
|  | ||||
| @ -37,6 +37,7 @@ request_configure: wl.Listener(*wlr.XwaylandSurface.event.Configure) = | ||||
| destroy: wl.Listener(*wlr.XwaylandSurface) = wl.Listener(*wlr.XwaylandSurface).init(handleDestroy), | ||||
| map: wl.Listener(*wlr.XwaylandSurface) = wl.Listener(*wlr.XwaylandSurface).init(handleMap), | ||||
| 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 { | ||||
|     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); | ||||
|     server.root.xwayland_unmanaged_views.prepend(node); | ||||
|  | ||||
|     xwayland_surface.surface.?.events.commit.add(&self.commit); | ||||
|  | ||||
|     // TODO: handle keyboard focus | ||||
|     // 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 | ||||
|     const node = @fieldParentPtr(std.TailQueue(Self).Node, "data", self); | ||||
|     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(); | ||||
| } | ||||
|  | ||||
| @ -232,6 +232,9 @@ fn handleRequestConfigure( | ||||
| /// TODO: check for unexpected change in size and react as needed | ||||
| fn handleCommit(listener: *wl.Listener(*wlr.Surface), surface: *wlr.Surface) void { | ||||
|     const self = @fieldParentPtr(Self, "commit", listener); | ||||
|  | ||||
|     self.view.output.damage.addWhole(); | ||||
|  | ||||
|     self.view.surface_box = Box{ | ||||
|         .x = 0, | ||||
|         .y = 0, | ||||
|  | ||||
| @ -53,7 +53,19 @@ pub fn renderOutput(output: *Output) void { | ||||
|     var now: os.timespec = undefined; | ||||
|     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)); | ||||
|  | ||||
|  | ||||
		Reference in New Issue
	
	Block a user