diff --git a/scripts/wlbg/src/DrawTimerHandler.zig b/scripts/wlbg/src/DrawTimerHandler.zig new file mode 100644 index 0000000..6c3ae4a --- /dev/null +++ b/scripts/wlbg/src/DrawTimerHandler.zig @@ -0,0 +1,41 @@ +const std = @import("std"); +const xev = @import("xev"); + +const options = @import("options.zig"); + +loop: *xev.Loop, + +/// Completion of the main redraw timer +completion: *xev.Completion, + +/// Contains a bool for each output, true if needs redraw +should_redraw: []bool, + +const DrawTimerHandler = @This(); + +pub fn nextAction(self: *DrawTimerHandler) xev.CallbackAction { + for (self.should_redraw) |ro| + if (ro) return .rearm; + return .disarm; +} + +pub fn maybeWake(self: *DrawTimerHandler) void { + if (self.completion.flags.state == .dead and self.nextAction() == .rearm) { + self.resetTimer(); + self.loop.add(self.completion); + } +} + +pub fn resetTimer(self: *DrawTimerHandler) void { + const next_time = self.loop.now() + 1000 / options.fps; + self.completion.op.timer.reset = .{ + .tv_sec = @divTrunc(next_time, std.time.ms_per_s), + .tv_nsec = @mod(next_time, std.time.ms_per_s) * std.time.ns_per_ms, + }; +} + +pub fn damage(self: *DrawTimerHandler, idx: usize) void { + self.should_redraw[idx] = true; + self.maybeWake(); +} + diff --git a/scripts/wlbg/src/Gfx.zig b/scripts/wlbg/src/Gfx.zig index 20f37e3..105192f 100644 --- a/scripts/wlbg/src/Gfx.zig +++ b/scripts/wlbg/src/Gfx.zig @@ -4,6 +4,7 @@ const c = @import("ffi.zig").c; const glutil = @import("glutil.zig"); const options = @import("options.zig"); +const DrawTimerHandler = @import("DrawTimerHandler.zig"); const OutputInfo = @import("OutputInfo.zig"); const OutputWindow = @import("OutputWindow.zig"); const PointerState = @import("PointerState.zig"); @@ -164,8 +165,11 @@ pub fn preDraw( pointer_state: *PointerState, outputs: []const OutputWindow, infos: []const OutputInfo, + dth: *DrawTimerHandler, ) !void { - for (self.cursor_positions, self.should_redraw, infos, outputs) |*pos, *redraw, inf, outp| { + for (self.cursor_positions, infos, outputs, 0..) |*pos, inf, outp, i| { + const lerp_amt = std.math.clamp(@as(f32, @floatFromInt(std.math.clamp(dt, 0, 10))) / 150.0, 0.0, 1.0); + const target = if (pointer_state.surface == outp.surface) .{ pointer_state.x, pointer_state.y } else @@ -174,16 +178,16 @@ pub fn preDraw( const new_x: c_int = @intFromFloat(std.math.lerp( @as(f32, @floatFromInt(pos[0])), @as(f32, @floatFromInt(target[0])), - std.math.clamp(@as(f32, @floatFromInt(dt)) / 250.0, 0.0, 1.0), + lerp_amt, )); const new_y: c_int = @intFromFloat(std.math.lerp( @as(f32, @floatFromInt(pos[1])), @as(f32, @floatFromInt(target[1])), - std.math.clamp(@as(f32, @floatFromInt(dt)) / 250.0, 0.0, 1.0), + lerp_amt, )); if (new_x != pos[0] or new_y != pos[1]) - redraw.* = true; + dth.damage(i); pos[0] = new_x; pos[1] = new_y; @@ -197,15 +201,13 @@ pub fn draw( output_idx: usize, outputs: []const OutputWindow, infos: []const OutputInfo, + dth: *DrawTimerHandler, ) !void { self.time += dt; + dth.should_redraw[output_idx] = false; c.glBindFramebuffer(c.GL_FRAMEBUFFER, 0); // use default framebuffer c.glUseProgram(self.main_shader_program); - if (!self.should_redraw[output_idx]) - return; - self.should_redraw[output_idx] = false; - const vertices = [_]f32{ -1.0, -1.0, 0.0, 0.0, 0.0, 1.0, -1.0, 0.0, 1.0, 0.0, diff --git a/scripts/wlbg/src/main.zig b/scripts/wlbg/src/main.zig index a930518..a1f9d43 100644 --- a/scripts/wlbg/src/main.zig +++ b/scripts/wlbg/src/main.zig @@ -5,6 +5,7 @@ const wayland = @import("wayland"); const c = @import("ffi.zig").c; const options = @import("options.zig"); +const DrawTimerHandler = @import("DrawTimerHandler.zig"); const Gfx = @import("Gfx.zig"); const Globals = @import("Globals.zig"); const OutputInfo = @import("OutputInfo.zig"); @@ -130,15 +131,32 @@ pub fn main() !void { } defer for (output_windows) |output| output.deinit(egl_ctx); + var r_timer_completion: xev.Completion = undefined; + var r_timer = try xev.Timer.init(); + defer r_timer.deinit(); + + var dth = DrawTimerHandler{ + .should_redraw = try std.heap.c_allocator.alloc(bool, output_info.len), + .completion = &r_timer_completion, + .loop = &loop, + }; + defer std.heap.c_allocator.free(dth.should_redraw); + var pointer_state = PointerState{ .surface = null, .x = 0, .y = 0, }; + var pointer_listener_data = PointerListenerData{ + .pstate = &pointer_state, + .outputs = output_windows, + .dth = &dth, + }; + const pointer = try globs.seat.getPointer(); defer pointer.destroy(); - pointer.setListener(*PointerState, pointerListener, &pointer_state); + pointer.setListener(*PointerListenerData, pointerListener, &pointer_listener_data); const base_offset: [2]i32 = off: { if (comptime options.multihead_mode == .individual) break :off .{ 0, 0 }; @@ -180,6 +198,7 @@ pub fn main() !void { .last_time = loop.now(), .base_offset = base_offset, .pointer_state = &pointer_state, + .dth = &dth, }; var rbg_timer_completion: xev.Completion = undefined; @@ -195,10 +214,6 @@ pub fn main() !void { renderBackgroundCb, ); - var r_timer_completion: xev.Completion = undefined; - var r_timer = try xev.Timer.init(); - defer r_timer.deinit(); - r_timer.run( &loop, &r_timer_completion, @@ -228,6 +243,7 @@ const RenderData = struct { last_time: isize, base_offset: [2]c_int, pointer_state: *PointerState, + dth: *DrawTimerHandler, }; fn renderCb( @@ -236,19 +252,21 @@ fn renderCb( completion: *xev.Completion, result: xev.Timer.RunError!void, ) xev.CallbackAction { + _ = completion; result catch unreachable; const now = loop.now(); const delta_time = now - data.?.last_time; data.?.last_time = now; - resetXevTimerCompletion(completion, now, 1000 / options.fps); + data.?.dth.resetTimer(); data.?.gfx.preDraw( delta_time, data.?.pointer_state, data.?.outputs, data.?.output_info, + data.?.dth, ) catch |e| { std.log.err("running preDraw: {}", .{e}); loop.stop(); @@ -256,6 +274,9 @@ fn renderCb( }; for (data.?.outputs, 0..) |output, i| { + if (!data.?.dth.should_redraw[i]) + continue; + if (c.eglMakeCurrent( data.?.egl_dpy, output.egl_surface, @@ -273,6 +294,7 @@ fn renderCb( i, data.?.outputs, data.?.output_info, + data.?.dth, ) catch |e| { std.log.err("drawing: {}", .{e}); loop.stop(); @@ -280,7 +302,7 @@ fn renderCb( }; } - return .rearm; + return data.?.dth.nextAction(); } fn renderBackgroundCb( @@ -359,14 +381,43 @@ fn xdgOutputListener(_: *zxdg.OutputV1, ev: zxdg.OutputV1.Event, info: *OutputIn } } -fn pointerListener(_: *wl.Pointer, ev: wl.Pointer.Event, state: *PointerState) void { +const PointerListenerData = struct { + pstate: *PointerState, + outputs: []const OutputWindow, + dth: *DrawTimerHandler, + + fn damageCurrentWindow(self: *PointerListenerData) !void { + if (self.pstate.surface) |ps| { + for (self.outputs, 0..) |o, i| { + if (ps == o.surface) { + self.dth.damage(i); + } + } + } + } +}; + +fn pointerListener(_: *wl.Pointer, ev: wl.Pointer.Event, d: *PointerListenerData) void { switch (ev) { .motion => |motion| { - state.x = motion.surface_x.toInt(); - state.y = motion.surface_y.toInt(); + d.pstate.x = motion.surface_x.toInt(); + d.pstate.y = motion.surface_y.toInt(); + d.damageCurrentWindow() catch |e| { + std.log.err("unable to damage window: {}", .{e}); + }; + }, + .enter => |enter| { + d.pstate.surface = enter.surface; + d.damageCurrentWindow() catch |e| { + std.log.err("unable to damage window: {}", .{e}); + }; + }, + .leave => { + d.damageCurrentWindow() catch |e| { + std.log.err("unable to damage window: {}", .{e}); + }; + d.pstate.surface = null; }, - .enter => |enter| state.surface = enter.surface, - .leave => state.surface = null, else => {}, } }