improve wlbg

This commit is contained in:
LordMZTE 2023-10-28 11:51:12 +02:00
parent 7c64072005
commit f2e10a974b
Signed by: LordMZTE
GPG key ID: B64802DC33A64FF6
8 changed files with 361 additions and 57 deletions

View file

@ -82,7 +82,7 @@ env = WLR_NO_HARDWARE_CURSORS, 1
<! end !>
exec-once = waybar
exec-once = swww-daemon
exec-once = wlbg
exec-once = sleep 1 && randomwallpaper
exec-once = hyprctl setcursor <% opt.cursor.theme %> <% opt.cursor.size %>
<! if opt.commands.notification_daemon.wl then !>

View file

@ -4,54 +4,237 @@ const c = @import("ffi.zig").c;
const glutil = @import("glutil.zig");
const OutputInfo = @import("OutputInfo.zig");
const OutputWindow = @import("OutputWindow.zig");
const PointerState = @import("PointerState.zig");
egl_dpy: c.EGLDisplay,
bg_shader_program: c_uint,
main_shader_program: c_uint,
bg_bufs: std.MultiArrayList(BgBuf),
time: i64,
cursor_positions: [][2]c_int,
const Gfx = @This();
pub fn init(egl_dpy: c.EGLDisplay) !Gfx {
const vert_shader = try glutil.createShader(c.GL_VERTEX_SHADER, @embedFile("bg_vert.glsl"));
defer c.glDeleteShader(vert_shader);
const frag_shader = try glutil.createShader(c.GL_FRAGMENT_SHADER, @embedFile("bg_frag.glsl"));
defer c.glDeleteShader(frag_shader);
const BgBuf = struct {
texture: c_uint,
framebuffer: c_uint,
zbuffer: c_uint,
};
const program = c.glCreateProgram();
errdefer c.glDeleteProgram(program);
c.glAttachShader(program, vert_shader);
c.glAttachShader(program, frag_shader);
c.glLinkProgram(program);
pub fn init(egl_dpy: c.EGLDisplay, output_info: []const OutputInfo) !Gfx {
const bg_program = shader: {
const vert_shader = try glutil.createShader(c.GL_VERTEX_SHADER, @embedFile("bg_vert.glsl"));
defer c.glDeleteShader(vert_shader);
const frag_shader = try glutil.createShader(c.GL_FRAGMENT_SHADER, @embedFile("bg_frag.glsl"));
defer c.glDeleteShader(frag_shader);
var success: c_int = 0;
c.glGetProgramiv(program, c.GL_LINK_STATUS, &success);
if (success != 1)
return error.ShaderLinkFail;
const program = c.glCreateProgram();
errdefer c.glDeleteProgram(program);
c.glAttachShader(program, vert_shader);
c.glAttachShader(program, frag_shader);
c.glLinkProgram(program);
var success: c_int = 0;
c.glGetProgramiv(program, c.GL_LINK_STATUS, &success);
if (success != 1)
return error.ShaderLinkFail;
break :shader program;
};
const main_program = shader: {
const vert_shader = try glutil.createShader(c.GL_VERTEX_SHADER, @embedFile("main_vert.glsl"));
defer c.glDeleteShader(vert_shader);
const frag_shader = try glutil.createShader(c.GL_FRAGMENT_SHADER, @embedFile("main_frag.glsl"));
defer c.glDeleteShader(frag_shader);
const program = c.glCreateProgram();
errdefer c.glDeleteProgram(program);
c.glAttachShader(program, vert_shader);
c.glAttachShader(program, frag_shader);
c.glLinkProgram(program);
var success: c_int = 0;
c.glGetProgramiv(program, c.GL_LINK_STATUS, &success);
if (success != 1)
return error.ShaderLinkFail;
break :shader program;
};
const cursor_positions = try std.heap.c_allocator.alloc([2]c_int, output_info.len);
errdefer std.heap.c_allocator.free(cursor_positions);
@memset(cursor_positions, .{ 0, 0 });
var bg_bufs = std.MultiArrayList(BgBuf){};
errdefer bg_bufs.deinit(std.heap.c_allocator);
try bg_bufs.resize(std.heap.c_allocator, output_info.len);
const bg_slice = bg_bufs.slice();
// @intCast safety: user is somewhat unlikely to have 2^32 - 1 monitors.
c.glGenTextures(@intCast(output_info.len), bg_slice.items(.texture).ptr);
errdefer c.glDeleteTextures(@intCast(bg_bufs.len), bg_slice.items(.texture).ptr);
c.glGenFramebuffers(@intCast(output_info.len), bg_slice.items(.framebuffer).ptr);
errdefer c.glDeleteFramebuffers(@intCast(bg_bufs.len), bg_slice.items(.framebuffer).ptr);
c.glGenRenderbuffers(@intCast(output_info.len), bg_slice.items(.zbuffer).ptr);
errdefer c.glDeleteRenderbuffers(@intCast(output_info.len), bg_slice.items(.zbuffer).ptr);
for (
output_info,
bg_slice.items(.texture),
bg_slice.items(.framebuffer),
bg_slice.items(.zbuffer),
) |inf, tex, fb, zb| {
c.glBindFramebuffer(c.GL_FRAMEBUFFER, fb);
c.glBindTexture(c.GL_TEXTURE_2D, tex);
c.glBindRenderbuffer(c.GL_RENDERBUFFER, zb);
c.glTexImage2D(
c.GL_TEXTURE_2D,
0,
c.GL_RGBA,
inf.width,
inf.height,
0,
c.GL_RGBA,
c.GL_UNSIGNED_BYTE,
null,
);
c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_MAG_FILTER, c.GL_NEAREST);
c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_MIN_FILTER, c.GL_NEAREST);
c.glFramebufferTexture2D(
c.GL_FRAMEBUFFER,
c.GL_COLOR_ATTACHMENT0,
c.GL_TEXTURE_2D,
tex,
0,
);
c.glRenderbufferStorage(c.GL_RENDERBUFFER, c.GL_DEPTH_COMPONENT16, inf.width, inf.height);
c.glFramebufferRenderbuffer(c.GL_FRAMEBUFFER, c.GL_DEPTH_ATTACHMENT, c.GL_RENDERBUFFER, zb);
if (c.glCheckFramebufferStatus(c.GL_FRAMEBUFFER) != c.GL_FRAMEBUFFER_COMPLETE)
return error.FramebufferIncomplete;
}
c.glBindFramebuffer(c.GL_FRAMEBUFFER, 0);
c.glBindTexture(c.GL_TEXTURE_2D, 0);
c.glBindRenderbuffer(c.GL_FRAMEBUFFER, 0);
return .{
.egl_dpy = egl_dpy,
.bg_shader_program = program,
.bg_shader_program = bg_program,
.main_shader_program = main_program,
.bg_bufs = bg_bufs,
.time = 0,
.cursor_positions = cursor_positions,
};
}
pub fn deinit(self: *Gfx) void {
const bg_slice = self.bg_bufs.slice();
c.glDeleteTextures(@intCast(bg_slice.len), bg_slice.items(.texture).ptr);
c.glDeleteFramebuffers(@intCast(bg_slice.len), bg_slice.items(.framebuffer).ptr);
c.glDeleteRenderbuffers(@intCast(bg_slice.len), bg_slice.items(.zbuffer).ptr);
self.bg_bufs.deinit(std.heap.c_allocator);
c.glDeleteProgram(self.bg_shader_program);
self.* = undefined;
}
pub fn drawBackground(
pub fn draw(
self: *Gfx,
dt: i64,
egl_surface: c.EGLSurface,
info: OutputInfo,
base_xoff: i32,
base_yoff: i32,
pointer_state: *PointerState,
output_idx: usize,
outputs: []const OutputWindow,
infos: []const OutputInfo,
) !void {
_ = dt;
self.time += dt;
c.glBindFramebuffer(c.GL_FRAMEBUFFER, 0); // use default framebuffer
c.glUseProgram(self.main_shader_program);
for (self.cursor_positions, infos, outputs) |*pos, inf, outp| {
const target = if (pointer_state.surface == outp.surface)
.{ pointer_state.x, pointer_state.y }
else
.{ @divTrunc(inf.width, 2), @divTrunc(inf.height, 2) };
pos[0] = @intFromFloat(std.math.lerp(
@as(f32, @floatFromInt(pos[0])),
@as(f32, @floatFromInt(target[0])),
std.math.clamp(@as(f32, @floatFromInt(dt)) / 1000.0, 0.0, 1.0),
));
pos[1] = @intFromFloat(std.math.lerp(
@as(f32, @floatFromInt(pos[1])),
@as(f32, @floatFromInt(target[1])),
std.math.clamp(@as(f32, @floatFromInt(dt)) / 1000.0, 0.0, 1.0),
));
}
const vertices = [_]f32{
-1.0, -1.0, 0.0, 0.0, 0.0,
1.0, -1.0, 0.0, 1.0, 0.0,
1.0, 1.0, 0.0, 1.0, 1.0,
-1.0, -1.0, 0.0, 0.0, 0.0,
1.0, 1.0, 0.0, 1.0, 1.0,
-1.0, 1.0, 0.0, 0.0, 1.0,
};
c.glVertexAttribPointer(0, 3, c.GL_FLOAT, c.GL_FALSE, @sizeOf(f32) * 5, &vertices);
c.glEnableVertexAttribArray(0);
c.glVertexAttribPointer(
1,
2,
c.GL_FLOAT,
c.GL_FALSE,
@sizeOf(f32) * 5,
@ptrFromInt(@intFromPtr(&vertices) + @sizeOf(f32) * 3),
);
c.glEnableVertexAttribArray(1);
c.glClearColor(1.0, 0.0, 0.0, 1.0);
c.glClear(c.GL_COLOR_BUFFER_BIT);
c.glBindTexture(c.GL_TEXTURE_2D, self.bg_bufs.get(output_idx).texture);
c.glUniform2f(
c.glGetUniformLocation(self.main_shader_program, "cursorPos"),
@as(f32, @floatFromInt(self.cursor_positions[output_idx][0])) / @as(f32, @floatFromInt(infos[output_idx].width)),
1.0 - @as(f32, @floatFromInt(self.cursor_positions[output_idx][1])) / @as(f32, @floatFromInt(infos[output_idx].height)),
);
c.glUniform1f(
c.glGetUniformLocation(self.main_shader_program, "hasCursor"),
if (outputs[output_idx].surface == pointer_state.surface) 1.0 else 0.0,
);
//c.glUniform1f(
// c.glGetUniformLocation(self.main_shader_program, "time"),
// @as(f32, @floatFromInt(self.time)) / 1000.0,
//);
c.glDrawArrays(c.GL_TRIANGLES, 0, vertices.len / 3);
if (c.eglSwapInterval(self.egl_dpy, 0) != c.EGL_TRUE or
c.eglSwapBuffers(self.egl_dpy, outputs[output_idx].egl_surface) != c.EGL_TRUE) return error.EGLError;
}
pub fn drawBackground(
self: *Gfx,
info: OutputInfo,
output_idx: usize,
base_off: [2]i32,
rand: f32,
) !void {
std.log.info("draw bg", .{});
// There's just about a 0% chance this works properly when monitors have different resolutions,
// but I can't even begin thinking about that.
const xoff = @as(f32, @floatFromInt(info.x - base_xoff)) / @as(f32, @floatFromInt(info.width));
const yoff = @as(f32, @floatFromInt(info.y - base_yoff)) / @as(f32, @floatFromInt(info.height));
const xoff = @as(f32, @floatFromInt(info.x - base_off[0])) / @as(f32, @floatFromInt(info.width));
const yoff = @as(f32, @floatFromInt(info.y - base_off[1])) / @as(f32, @floatFromInt(info.height));
const vertices = [_]f32{
-1.0, -1.0, 0.0, xoff, yoff,
@ -63,6 +246,8 @@ pub fn drawBackground(
-1.0, 1.0, 0.0, xoff, yoff,
};
c.glBindFramebuffer(c.GL_FRAMEBUFFER, self.bg_bufs.get(output_idx).framebuffer);
c.glClearColor(1.0, 0.0, 0.0, 1.0);
c.glClear(c.GL_COLOR_BUFFER_BIT);
@ -74,11 +259,7 @@ pub fn drawBackground(
c.glVertexAttribPointer(1, 2, c.GL_FLOAT, c.GL_FALSE, @sizeOf(f32) * 5, @ptrFromInt(@intFromPtr(&vertices) + @sizeOf(f32) * 3));
c.glEnableVertexAttribArray(1);
const rand = std.crypto.random.float(f32);
c.glUniform1f(c.glGetUniformLocation(self.bg_shader_program, "time"), rand * 2000.0 - 1000.0);
c.glDrawArrays(c.GL_TRIANGLES, 0, vertices.len / 3);
if (c.eglSwapInterval(self.egl_dpy, 0) != c.EGL_TRUE or
c.eglSwapBuffers(self.egl_dpy, egl_surface) != c.EGL_TRUE) return error.EGLError;
}

View file

@ -7,6 +7,7 @@ const zwlr = wayland.client.zwlr;
const log = std.log.scoped(.globals);
seat: *wl.Seat,
compositor: *wl.Compositor,
layer_shell: *zwlr.LayerShellV1,
xdg_output_manager: *zxdg.OutputManagerV1,

View file

@ -0,0 +1,14 @@
const wl = @import("wayland").client.wl;
const c = @import("ffi.zig").c;
egl_win: *wl.EglWindow,
egl_surface: c.EGLSurface,
surface: *wl.Surface,
const OutputWindow = @This();
pub fn deinit(self: OutputWindow, egl_dpy: c.EGLDisplay) void {
self.egl_win.destroy();
self.surface.destroy();
_ = c.eglDestroySurface(egl_dpy, self.egl_surface);
}

View file

@ -0,0 +1,5 @@
const wl = @import("wayland").client.wl;
surface: ?*wl.Surface,
x: c_int,
y: c_int,

View file

@ -7,11 +7,19 @@ const c = @import("ffi.zig").c;
const Gfx = @import("Gfx.zig");
const Globals = @import("Globals.zig");
const OutputInfo = @import("OutputInfo.zig");
const OutputWindow = @import("OutputWindow.zig");
const PointerState = @import("PointerState.zig");
const wl = wayland.client.wl;
const zwlr = wayland.client.zwlr;
const zxdg = wayland.client.zxdg;
pub const std_options = struct {
pub const log_level = .debug;
};
const fps = 15;
pub fn main() !void {
std.log.info("initializing event loop", .{});
var loop = try xev.Loop.init(.{});
@ -118,10 +126,21 @@ pub fn main() !void {
output_window.* = .{
.egl_win = egl_win,
.egl_surface = egl_surface,
.surface = surface,
};
}
defer for (output_windows) |output| output.deinit(egl_ctx);
var pointer_state = PointerState{
.surface = null,
.x = 0,
.y = 0,
};
const pointer = try globs.seat.getPointer();
defer pointer.destroy();
pointer.setListener(*PointerState, pointerListener, &pointer_state);
var total_width: i32 = 0;
var total_height: i32 = 0;
@ -145,10 +164,10 @@ pub fn main() !void {
egl_ctx,
) != c.EGL_TRUE) return error.EGLError;
var gfx = try Gfx.init(egl_dpy);
var gfx = try Gfx.init(egl_dpy, output_info);
defer gfx.deinit();
var rbgdata = RenderBackgroundData{
var rdata = RenderData{
.gfx = &gfx,
.egl_dpy = egl_dpy,
.egl_ctx = egl_ctx,
@ -156,6 +175,7 @@ pub fn main() !void {
.output_info = output_info,
.last_time = loop.now(),
.base_offset = .{ base_xoff, base_yoff },
.pointer_state = &pointer_state,
};
var rbg_timer_completion: xev.Completion = undefined;
@ -166,11 +186,24 @@ pub fn main() !void {
&loop,
&rbg_timer_completion,
0,
RenderBackgroundData,
&rbgdata,
RenderData,
&rdata,
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,
0,
RenderData,
&rdata,
renderCb,
);
var wl_poll_completion = xev.Completion{
.op = .{ .poll = .{ .fd = dpy.getFd() } },
.userdata = dpy,
@ -182,7 +215,7 @@ pub fn main() !void {
try loop.run(.until_done);
}
const RenderBackgroundData = struct {
const RenderData = struct {
gfx: *Gfx,
egl_dpy: c.EGLDisplay,
egl_ctx: c.EGLContext,
@ -190,10 +223,11 @@ const RenderBackgroundData = struct {
output_info: []const OutputInfo,
last_time: isize,
base_offset: [2]c_int,
pointer_state: *PointerState,
};
fn renderBackgroundCb(
data: ?*RenderBackgroundData,
fn renderCb(
data: ?*RenderData,
loop: *xev.Loop,
completion: *xev.Completion,
result: xev.Timer.RunError!void,
@ -201,16 +235,12 @@ fn renderBackgroundCb(
result catch unreachable;
const now = loop.now();
const delta_time = now - data.?.last_time;
data.?.last_time = now;
const next_time = now + std.time.ms_per_min;
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,
};
for (data.?.outputs, data.?.output_info) |output, info| {
resetXevTimerCompletion(completion, now, 1000 / fps);
for (data.?.outputs, 0..) |output, i| {
if (c.eglMakeCurrent(
data.?.egl_dpy,
output.egl_surface,
@ -222,12 +252,12 @@ fn renderBackgroundCb(
return .disarm;
}
data.?.gfx.drawBackground(
data.?.gfx.draw(
delta_time,
output.egl_surface,
info,
data.?.base_offset[0],
data.?.base_offset[1],
data.?.pointer_state,
i,
data.?.outputs,
data.?.output_info,
) catch |e| {
std.log.err("drawing: {}", .{e});
loop.stop();
@ -238,6 +268,34 @@ fn renderBackgroundCb(
return .rearm;
}
fn renderBackgroundCb(
data: ?*RenderData,
loop: *xev.Loop,
completion: *xev.Completion,
result: xev.Timer.RunError!void,
) xev.CallbackAction {
result catch unreachable;
resetXevTimerCompletion(completion, loop.now(), std.time.ms_per_min);
const rand = std.crypto.random.float(f32);
for (data.?.outputs, data.?.output_info, 0..) |output, info, i| {
_ = output;
data.?.gfx.drawBackground(
info,
i,
data.?.base_offset,
rand,
) catch |e| {
std.log.err("drawing background: {}", .{e});
loop.stop();
return .disarm;
};
}
return .rearm;
}
fn wlPollCb(
userdata: ?*anyopaque,
loop: *xev.Loop,
@ -260,16 +318,6 @@ fn wlPollCb(
return .rearm;
}
const OutputWindow = struct {
egl_win: *wl.EglWindow,
egl_surface: c.EGLSurface,
fn deinit(self: OutputWindow, egl_dpy: c.EGLDisplay) void {
self.egl_win.destroy();
_ = c.eglDestroySurface(egl_dpy, self.egl_surface);
}
};
fn layerSurfaceListener(lsurf: *zwlr.LayerSurfaceV1, ev: zwlr.LayerSurfaceV1.Event, winsize: *?[2]c_int) void {
switch (ev) {
.configure => |configure| {
@ -294,3 +342,23 @@ fn xdgOutputListener(_: *zxdg.OutputV1, ev: zxdg.OutputV1.Event, info: *OutputIn
else => {},
}
}
fn pointerListener(_: *wl.Pointer, ev: wl.Pointer.Event, state: *PointerState) void {
switch (ev) {
.motion => |motion| {
state.x = motion.surface_x.toInt();
state.y = motion.surface_y.toInt();
},
.enter => |enter| state.surface = enter.surface,
.leave => state.surface = null,
else => {},
}
}
fn resetXevTimerCompletion(completion: *xev.Completion, now: i64, in: i64) void {
const next_time = now + in;
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,
};
}

View file

@ -0,0 +1,20 @@
#version 300 es
precision highp float;
uniform sampler2D bg;
uniform vec2 cursorPos;
uniform float hasCursor;
in vec2 fragCoord;
in vec2 fragUv;
void main() {
vec2 diff = cursorPos - fragUv;
float light = clamp(.1 - (diff.x * diff.x + diff.y * diff.y), 0.0, 1.0);
vec2 zoomedUv = fragUv * .9 + (.1 / 2.0);
gl_FragColor = mix(texture(bg, zoomedUv + (cursorPos - .5) / 10.0), vec4(1.0), light * hasCursor);
}

View file

@ -0,0 +1,15 @@
#version 300 es
precision mediump float;
attribute vec4 vPos;
attribute vec2 uv;
out vec2 fragCoord;
out vec2 fragUv;
void main() {
gl_Position = vPos;
fragCoord = vPos.xy;
fragUv = uv;
}