wlbg: completely remove draw handler from event loop in idle state

the process is completely blocked with no interaction happening
This commit is contained in:
LordMZTE 2023-10-28 22:48:08 +02:00
parent 733b7cf4ce
commit 8a2e617f39
Signed by: LordMZTE
GPG key ID: B64802DC33A64FF6
3 changed files with 114 additions and 20 deletions

View file

@ -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();
}

View file

@ -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,

View file

@ -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 => {},
}
}