diff --git a/river/Cursor.zig b/river/Cursor.zig index 810d360..ca8745e 100644 --- a/river/Cursor.zig +++ b/river/Cursor.zig @@ -31,6 +31,7 @@ const server = &@import("main.zig").server; const util = @import("util.zig"); const Config = @import("Config.zig"); +const DragIcon = @import("DragIcon.zig"); const LayerSurface = @import("LayerSurface.zig"); const LockSurface = @import("LockSurface.zig"); const Output = @import("Output.zig"); @@ -523,6 +524,8 @@ fn handleTouchMotion( log.err("out of memory", .{}); }; + self.updateDragIcons(); + if (server.root.at(lx, ly)) |result| { self.seat.wlr_seat.touchNotifyMotion(event.time_msec, event.touch_id, result.sx, result.sy); } @@ -721,6 +724,7 @@ fn processMotion(self: *Self, device: *wlr.InputDevice, time: u32, delta_x: f64, unaccel_dx, unaccel_dy, ); + var dx: f64 = delta_x; var dy: f64 = delta_y; switch (self.mode) { @@ -728,6 +732,7 @@ fn processMotion(self: *Self, device: *wlr.InputDevice, time: u32, delta_x: f64, self.wlr_cursor.move(device, dx, dy); self.checkFocusFollowsCursor(); self.passthrough(time); + self.updateDragIcons(); }, .down => |down| { self.wlr_cursor.move(device, dx, dy); @@ -736,6 +741,7 @@ fn processMotion(self: *Self, device: *wlr.InputDevice, time: u32, delta_x: f64, down.sx + (self.wlr_cursor.x - down.lx), down.sy + (self.wlr_cursor.y - down.ly), ); + self.updateDragIcons(); }, .move => |*data| { dx += data.delta_x; @@ -933,3 +939,30 @@ fn warp(self: *Self) void { } } } + +fn updateDragIcons(self: *Self) void { + var it = server.root.layers.drag_icons.children.iterator(.forward); + while (it.next()) |node| { + const icon = @intToPtr(*DragIcon, node.data); + + if (icon.wlr_drag_icon.drag.seat != self.seat.wlr_seat) continue; + + switch (icon.wlr_drag_icon.drag.grab_type) { + .keyboard => unreachable, + .keyboard_pointer => { + icon.tree.node.setPosition( + @floatToInt(c_int, self.wlr_cursor.x), + @floatToInt(c_int, self.wlr_cursor.y), + ); + }, + .keyboard_touch => { + const touch_id = icon.wlr_drag_icon.drag.touch_id; + const point = self.touch_points.get(touch_id) orelse continue; + icon.tree.node.setPosition( + @floatToInt(c_int, point.lx), + @floatToInt(c_int, point.ly), + ); + }, + } + } +} diff --git a/river/DragIcon.zig b/river/DragIcon.zig index 11683ea..bba55f2 100644 --- a/river/DragIcon.zig +++ b/river/DragIcon.zig @@ -23,40 +23,70 @@ const wl = @import("wayland").server.wl; const server = &@import("main.zig").server; const util = @import("util.zig"); -const Seat = @import("Seat.zig"); +const SceneNodeData = @import("SceneNodeData.zig"); -seat: *Seat, wlr_drag_icon: *wlr.Drag.Icon, -// Accumulated x/y surface offset from the cursor/touch point position. -sx: i32 = 0, -sy: i32 = 0, +tree: *wlr.SceneTree, +surface: *wlr.SceneTree, -// Always active destroy: wl.Listener(*wlr.Drag.Icon) = wl.Listener(*wlr.Drag.Icon).init(handleDestroy), +map: wl.Listener(*wlr.Drag.Icon) = wl.Listener(*wlr.Drag.Icon).init(handleMap), +unmap: wl.Listener(*wlr.Drag.Icon) = wl.Listener(*wlr.Drag.Icon).init(handleUnmap), commit: wl.Listener(*wlr.Surface) = wl.Listener(*wlr.Surface).init(handleCommit), -pub fn init(drag_icon: *DragIcon, seat: *Seat, wlr_drag_icon: *wlr.Drag.Icon) void { - drag_icon.* = .{ .seat = seat, .wlr_drag_icon = wlr_drag_icon }; +pub fn create(wlr_drag_icon: *wlr.Drag.Icon) error{OutOfMemory}!void { + const tree = try server.root.layers.drag_icons.createSceneTree(); + errdefer tree.node.destroy(); + + const drag_icon = try util.gpa.create(DragIcon); + errdefer util.gpa.destroy(drag_icon); + + drag_icon.* = .{ + .wlr_drag_icon = wlr_drag_icon, + .tree = tree, + .surface = try tree.createSceneSubsurfaceTree(wlr_drag_icon.surface), + }; + tree.node.data = @ptrToInt(drag_icon); + + tree.node.setEnabled(wlr_drag_icon.mapped); wlr_drag_icon.events.destroy.add(&drag_icon.destroy); + wlr_drag_icon.events.map.add(&drag_icon.map); + wlr_drag_icon.events.unmap.add(&drag_icon.unmap); wlr_drag_icon.surface.events.commit.add(&drag_icon.commit); } fn handleDestroy(listener: *wl.Listener(*wlr.Drag.Icon), _: *wlr.Drag.Icon) void { const drag_icon = @fieldParentPtr(DragIcon, "destroy", listener); - drag_icon.seat.drag_icon = null; + drag_icon.tree.node.destroy(); drag_icon.destroy.link.remove(); + drag_icon.map.link.remove(); + drag_icon.unmap.link.remove(); drag_icon.commit.link.remove(); util.gpa.destroy(drag_icon); } +fn handleMap(listener: *wl.Listener(*wlr.Drag.Icon), _: *wlr.Drag.Icon) void { + const drag_icon = @fieldParentPtr(DragIcon, "map", listener); + + drag_icon.tree.node.setEnabled(true); +} + +fn handleUnmap(listener: *wl.Listener(*wlr.Drag.Icon), _: *wlr.Drag.Icon) void { + const drag_icon = @fieldParentPtr(DragIcon, "unmap", listener); + + drag_icon.tree.node.setEnabled(false); +} + fn handleCommit(listener: *wl.Listener(*wlr.Surface), surface: *wlr.Surface) void { const drag_icon = @fieldParentPtr(DragIcon, "commit", listener); - drag_icon.sx += surface.current.dx; - drag_icon.sy += surface.current.dy; + drag_icon.surface.node.setPosition( + drag_icon.surface.node.x + surface.current.dx, + drag_icon.surface.node.y + surface.current.dy, + ); } diff --git a/river/Output.zig b/river/Output.zig index 036495c..2917bad 100644 --- a/river/Output.zig +++ b/river/Output.zig @@ -68,6 +68,7 @@ tree: *wlr.SceneTree, normal_content: *wlr.SceneTree, locked_content: *wlr.SceneTree, +/// Child nodes of normal_content layers: struct { background_color_rect: *wlr.SceneRect, /// Background layer shell layer @@ -82,7 +83,7 @@ layers: struct { fullscreen: *wlr.SceneTree, /// Overlay layer shell layer overlay: *wlr.SceneTree, - /// Xdg popups, Xwayland override redirect windows + /// xdg-popups of views and layer-shell surfaces popups: *wlr.SceneTree, }, @@ -166,7 +167,7 @@ pub fn create(wlr_output: *wlr.Output) !void { var height: c_int = undefined; wlr_output.effectiveResolution(&width, &height); - const tree = try server.root.scene.tree.createSceneTree(); + const tree = try server.root.layers.outputs.createSceneTree(); const normal_content = try tree.createSceneTree(); self.* = .{ diff --git a/river/Root.zig b/river/Root.zig index 4e1e5b9..01a2b10 100644 --- a/river/Root.zig +++ b/river/Root.zig @@ -37,6 +37,16 @@ const ViewStack = @import("view_stack.zig").ViewStack; const XwaylandOverrideRedirect = @import("XwaylandOverrideRedirect.zig"); scene: *wlr.Scene, +/// All direct children of the root scene node +layers: struct { + /// Parent tree for output trees which have their position updated when + /// outputs are moved in the layout. + outputs: *wlr.SceneTree, + /// Drag icons which have a position in layout coordinates that is updated + /// on cursor/touch point movement. + /// This tree is ignored by Root.at() + drag_icons: *wlr.SceneTree, +}, new_output: wl.Listener(*wlr.Output) = wl.Listener(*wlr.Output).init(handleNewOutput), @@ -94,12 +104,19 @@ pub fn init(self: *Self) !void { const transaction_timer = try event_loop.addTimer(*Self, handleTransactionTimeout, self); errdefer transaction_timer.remove(); + const outputs = try scene.tree.createSceneTree(); + // TODO get rid of this hack somehow const noop_wlr_output = try server.headless_backend.headlessAddOutput(1920, 1080); - const noop_tree = try scene.tree.createSceneTree(); + const noop_tree = try outputs.createSceneTree(); noop_tree.node.setEnabled(false); + self.* = .{ .scene = scene, + .layers = .{ + .outputs = outputs, + .drag_icons = try scene.tree.createSceneTree(), + }, .output_layout = output_layout, .output_manager = try wlr.OutputManagerV1.create(server.wl_server), .power_manager = try wlr.OutputPowerManagerV1.create(server.wl_server), @@ -153,11 +170,12 @@ pub const AtResult = struct { }, }; -/// Return information about what is currently rendered at the given layout coordinates. +/// Return information about what is currently rendered in the Root.layers.outputs +/// tree at the given layout coordinates. pub fn at(self: Self, lx: f64, ly: f64) ?AtResult { var sx: f64 = undefined; var sy: f64 = undefined; - const node_at = self.scene.tree.node.at(lx, ly, &sx, &sy) orelse return null; + const node_at = self.layers.outputs.node.at(lx, ly, &sx, &sy) orelse return null; const surface: ?*wlr.Surface = blk: { if (node_at.type == .buffer) { diff --git a/river/Seat.zig b/river/Seat.zig index d53532c..3dd6883 100644 --- a/river/Seat.zig +++ b/river/Seat.zig @@ -92,7 +92,6 @@ drag: enum { pointer, touch, } = .none, -drag_icon: ?*DragIcon = null, request_set_selection: wl.Listener(*wlr.Seat.event.RequestSetSelection) = wl.Listener(*wlr.Seat.event.RequestSetSelection).init(handleRequestSetSelection), @@ -573,13 +572,11 @@ fn handleStartDrag(listener: *wl.Listener(*wlr.Drag), wlr_drag: *wlr.Drag) void wlr_drag.events.destroy.add(&self.drag_destroy); if (wlr_drag.icon) |wlr_drag_icon| { - const drag_icon = util.gpa.create(DragIcon) catch { + DragIcon.create(wlr_drag_icon) catch { log.err("out of memory", .{}); wlr_drag.seat_client.client.postNoMemory(); return; }; - drag_icon.init(self, wlr_drag_icon); - self.drag_icon = drag_icon; } }