mirror of
https://mzte.de/git/LordMZTE/dotfiles.git
synced 2024-12-12 21:52:57 +01:00
implement wlbg
This commit is contained in:
parent
e763bfbcc4
commit
1e04919f26
13 changed files with 944 additions and 0 deletions
1
scripts/wlbg/.gitignore
vendored
Normal file
1
scripts/wlbg/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/zig-*
|
49
scripts/wlbg/build.zig
Normal file
49
scripts/wlbg/build.zig
Normal file
|
@ -0,0 +1,49 @@
|
|||
const std = @import("std");
|
||||
|
||||
const Scanner = @import("wayland").Scanner;
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
const scanner = Scanner.create(b, .{});
|
||||
const wayland_mod = b.createModule(.{ .source_file = scanner.result });
|
||||
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "wlbg",
|
||||
.root_source_file = .{ .path = "src/main.zig" },
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
exe.addModule("wayland", wayland_mod);
|
||||
|
||||
scanner.addSystemProtocol("stable/xdg-shell/xdg-shell.xml");
|
||||
scanner.addSystemProtocol("unstable/xdg-output/xdg-output-unstable-v1.xml");
|
||||
scanner.addCustomProtocol("wlr-layer-shell-unstable-v1.xml");
|
||||
|
||||
scanner.generate("wl_compositor", 5);
|
||||
scanner.generate("wl_shm", 1);
|
||||
scanner.generate("zwlr_layer_shell_v1", 4);
|
||||
scanner.generate("zxdg_output_manager_v1", 3);
|
||||
scanner.generate("xdg_wm_base", 5); // dependency of layer shell
|
||||
scanner.generate("wl_seat", 8);
|
||||
scanner.generate("wl_output", 4);
|
||||
|
||||
exe.linkLibC();
|
||||
exe.linkSystemLibrary("wayland-client");
|
||||
exe.linkSystemLibrary("wayland-egl");
|
||||
exe.linkSystemLibrary("EGL");
|
||||
exe.linkSystemLibrary("GLESv2");
|
||||
scanner.addCSource(exe);
|
||||
|
||||
b.installArtifact(exe);
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
run_cmd.step.dependOn(b.getInstallStep());
|
||||
if (b.args) |args| {
|
||||
run_cmd.addArgs(args);
|
||||
}
|
||||
|
||||
const run_step = b.step("run", "Run the app");
|
||||
run_step.dependOn(&run_cmd.step);
|
||||
}
|
11
scripts/wlbg/build.zig.zon
Normal file
11
scripts/wlbg/build.zig.zon
Normal file
|
@ -0,0 +1,11 @@
|
|||
.{
|
||||
.name = "wlbg",
|
||||
.version = "0.0.0",
|
||||
.paths = .{""},
|
||||
.dependencies = .{
|
||||
.wayland = .{
|
||||
.url = "https://git.mzte.de/LordMZTE/zig-wayland/archive/85722422985f928087e56d90c3617ecb04232486.tar.gz",
|
||||
.hash = "1220d992b223e473988d203d66d262e54141b59559c09587eb00231c800d46f9b408",
|
||||
},
|
||||
},
|
||||
}
|
84
scripts/wlbg/src/Gfx.zig
Normal file
84
scripts/wlbg/src/Gfx.zig
Normal file
|
@ -0,0 +1,84 @@
|
|||
const std = @import("std");
|
||||
const c = @import("ffi.zig").c;
|
||||
|
||||
const glutil = @import("glutil.zig");
|
||||
|
||||
const OutputInfo = @import("OutputInfo.zig");
|
||||
|
||||
egl_dpy: c.EGLDisplay,
|
||||
bg_shader_program: c_uint,
|
||||
time: f64,
|
||||
|
||||
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 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;
|
||||
|
||||
return .{
|
||||
.egl_dpy = egl_dpy,
|
||||
.bg_shader_program = program,
|
||||
.time = 0.0,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Gfx) void {
|
||||
c.glDeleteProgram(self.bg_shader_program);
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
pub fn draw(
|
||||
self: *Gfx,
|
||||
dt: f32,
|
||||
egl_surface: c.EGLSurface,
|
||||
info: OutputInfo,
|
||||
base_xoff: i32,
|
||||
base_yoff: i32,
|
||||
) !void {
|
||||
self.time += dt;
|
||||
|
||||
// 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 vertices = [_]f32{
|
||||
-1.0, -1.0, 0.0, xoff, yoff,
|
||||
1.0, -1.0, 0.0, xoff, yoff,
|
||||
1.0, 1.0, 0.0, xoff, yoff,
|
||||
|
||||
-1.0, -1.0, 0.0, xoff, yoff,
|
||||
1.0, 1.0, 0.0, xoff, yoff,
|
||||
-1.0, 1.0, 0.0, xoff, yoff,
|
||||
};
|
||||
|
||||
c.glClearColor(1.0, 0.0, 0.0, 1.0);
|
||||
c.glClear(c.GL_COLOR_BUFFER_BIT);
|
||||
|
||||
c.glUseProgram(self.bg_shader_program);
|
||||
|
||||
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.glUniform1f(c.glGetUniformLocation(self.bg_shader_program, "time"), @as(f32, @floatCast(self.time / 10000.0)));
|
||||
|
||||
c.glDrawArrays(c.GL_TRIANGLES, 0, vertices.len / 3);
|
||||
|
||||
if (c.eglSwapBuffers(self.egl_dpy, egl_surface) != c.EGL_TRUE) return error.EGLError;
|
||||
}
|
103
scripts/wlbg/src/Globals.zig
Normal file
103
scripts/wlbg/src/Globals.zig
Normal file
|
@ -0,0 +1,103 @@
|
|||
const std = @import("std");
|
||||
const wayland = @import("wayland");
|
||||
|
||||
const wl = wayland.client.wl;
|
||||
const zxdg = wayland.client.zxdg;
|
||||
const zwlr = wayland.client.zwlr;
|
||||
|
||||
const log = std.log.scoped(.globals);
|
||||
|
||||
compositor: *wl.Compositor,
|
||||
layer_shell: *zwlr.LayerShellV1,
|
||||
xdg_output_manager: *zxdg.OutputManagerV1,
|
||||
outputs: std.ArrayList(*wl.Output),
|
||||
|
||||
const Globals = @This();
|
||||
|
||||
const Collector = col: {
|
||||
var fields: []const std.builtin.Type.StructField = &.{};
|
||||
for (std.meta.fields(Globals)) |field| {
|
||||
const Field = @Type(.{ .Optional = .{ .child = field.type } });
|
||||
fields = fields ++ [1]std.builtin.Type.StructField{.{
|
||||
.name = field.name,
|
||||
.type = Field,
|
||||
.default_value = @ptrCast(&@as(Field, null)),
|
||||
.is_comptime = false,
|
||||
.alignment = @alignOf(Field),
|
||||
}};
|
||||
}
|
||||
break :col @Type(.{ .Struct = .{
|
||||
.layout = .Auto,
|
||||
.fields = fields,
|
||||
.decls = &.{},
|
||||
.is_tuple = false,
|
||||
} });
|
||||
};
|
||||
|
||||
pub fn collect(dpy: *wl.Display) !Globals {
|
||||
const registry = try dpy.getRegistry();
|
||||
defer registry.destroy();
|
||||
|
||||
var col = Collector{};
|
||||
|
||||
inline for (std.meta.fields(Globals)) |f| {
|
||||
if (comptime isList(f.type)) {
|
||||
@field(col, f.name) = f.type.init(std.heap.c_allocator);
|
||||
}
|
||||
}
|
||||
|
||||
errdefer inline for (std.meta.fields(Globals)) |f| {
|
||||
if (comptime isList(f.type)) {
|
||||
@field(col, f.name).?.deinit();
|
||||
}
|
||||
};
|
||||
|
||||
registry.setListener(*Collector, registryListener, &col);
|
||||
|
||||
if (dpy.roundtrip() != .SUCCESS) return error.RoundtipFail;
|
||||
|
||||
// TODO don't use undefined. std.mem.zeroInit complains about non-nullable pointers in struct
|
||||
var self: Globals = undefined;
|
||||
inline for (std.meta.fields(Globals)) |f| {
|
||||
@field(self, f.name) = @field(col, f.name) orelse return error.MissingGlobals;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
fn registryListener(reg: *wl.Registry, ev: wl.Registry.Event, col: *Collector) void {
|
||||
switch (ev) {
|
||||
.global => |global| {
|
||||
inline for (std.meta.fields(Collector)) |f| {
|
||||
const BaseType = std.meta.Child(f.type);
|
||||
const Interface = std.meta.Child(if (comptime isList(BaseType))
|
||||
std.meta.Child(std.meta.FieldType(BaseType, .items))
|
||||
else
|
||||
BaseType);
|
||||
if (std.mem.orderZ(u8, global.interface, Interface.getInterface().name) == .eq) {
|
||||
log.info("binding global {s}@{}", .{ Interface.getInterface().name, global.name });
|
||||
|
||||
const bound = reg.bind(
|
||||
global.name,
|
||||
Interface,
|
||||
Interface.generated_version,
|
||||
) catch return;
|
||||
|
||||
if (comptime isList(BaseType)) {
|
||||
@field(col, f.name).?.append(bound) catch @panic("OOM");
|
||||
} else {
|
||||
@field(col, f.name) = bound;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
.global_remove => {},
|
||||
}
|
||||
}
|
||||
|
||||
fn isList(comptime T: type) bool {
|
||||
return @typeInfo(T) == .Struct and
|
||||
@hasDecl(T, "init") and
|
||||
@hasDecl(T, "deinit") and
|
||||
@hasDecl(T, "append");
|
||||
}
|
10
scripts/wlbg/src/OutputInfo.zig
Normal file
10
scripts/wlbg/src/OutputInfo.zig
Normal file
|
@ -0,0 +1,10 @@
|
|||
const std = @import("std");
|
||||
const wayland = @import("wayland");
|
||||
|
||||
const wl = wayland.client.wl;
|
||||
const xdg = wayland.client.xdg;
|
||||
|
||||
x: i32 = 0,
|
||||
y: i32 = 0,
|
||||
width: i32 = 0,
|
||||
height: i32 = 0,
|
43
scripts/wlbg/src/bg_frag.glsl
Normal file
43
scripts/wlbg/src/bg_frag.glsl
Normal file
|
@ -0,0 +1,43 @@
|
|||
#version 300 es
|
||||
|
||||
precision mediump float;
|
||||
|
||||
uniform float time;
|
||||
uniform vec2 offset;
|
||||
|
||||
in vec2 fragCoord;
|
||||
|
||||
/* "Quasar" by @kishimisu (2023) - https://www.shadertoy.com/view/msGyzc
|
||||
449 => 443 chars thanks to @Xor
|
||||
|
||||
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
|
||||
*/
|
||||
|
||||
#define r(a) mat2(cos(a + asin(vec4(0,1,-1,0)))),
|
||||
#define X(p) p *= r(round(atan(p.x, p.y) * 4.) / 4.)
|
||||
|
||||
void main() {
|
||||
vec2 F = fragCoord - offset + .5;
|
||||
vec3 p, R = vec3(1.);
|
||||
float i, t, d, a, b, T = time * .5 + .5;
|
||||
|
||||
vec4 O = vec4(0.);
|
||||
|
||||
for(O *= i; i++ < 44.;
|
||||
O += .04 * (1. + cos(a + t*.3 - T*.8 + vec4(0,1,2,0)))
|
||||
/ (1. + abs(d)*30.) )
|
||||
|
||||
p = t * normalize(vec3(F+F-R.xy, R.y)),
|
||||
p.z -= 4.,
|
||||
p.xz *= r(T/4.)
|
||||
p.yz *= r(sin(T/4.)*.5)
|
||||
X(p.zx) a = p.x,
|
||||
X(p.yx)
|
||||
p.x = mod(b = p.x - T, .5) - .25,
|
||||
|
||||
t += d = length(p) - (2. - a - smoothstep(b+2., b, T)*30.)
|
||||
* (cos(T/6.+1.)+1.) / 2e2;
|
||||
|
||||
gl_FragColor = O;
|
||||
}
|
||||
|
13
scripts/wlbg/src/bg_vert.glsl
Normal file
13
scripts/wlbg/src/bg_vert.glsl
Normal file
|
@ -0,0 +1,13 @@
|
|||
#version 300 es
|
||||
|
||||
precision mediump float;
|
||||
|
||||
attribute vec4 vPos;
|
||||
attribute vec2 monitorOffset;
|
||||
|
||||
out vec2 fragCoord;
|
||||
|
||||
void main() {
|
||||
gl_Position = vPos;
|
||||
fragCoord = vPos.xy + monitorOffset * 2.0;
|
||||
}
|
5
scripts/wlbg/src/ffi.zig
Normal file
5
scripts/wlbg/src/ffi.zig
Normal file
|
@ -0,0 +1,5 @@
|
|||
pub const c = @cImport({
|
||||
@cInclude("wayland-egl.h"); // required for egl include to work
|
||||
@cInclude("EGL/egl.h");
|
||||
@cInclude("GLES2/gl2.h");
|
||||
});
|
24
scripts/wlbg/src/glutil.zig
Normal file
24
scripts/wlbg/src/glutil.zig
Normal file
|
@ -0,0 +1,24 @@
|
|||
const std = @import("std");
|
||||
const c = @import("ffi.zig").c;
|
||||
|
||||
pub fn createShader(shadertype: c_uint, src: []const u8) !c_uint {
|
||||
const id = c.glCreateShader(shadertype);
|
||||
c.glShaderSource(
|
||||
id,
|
||||
1,
|
||||
&[_][*]const u8{src.ptr},
|
||||
&[_]c_int{@as(c_int, @intCast(src.len))},
|
||||
);
|
||||
c.glCompileShader(id);
|
||||
|
||||
var success: c_int = 0;
|
||||
c.glGetShaderiv(id, c.GL_COMPILE_STATUS, &success);
|
||||
|
||||
if (success == 0) {
|
||||
var info_log = std.mem.zeroes([512:0]u8);
|
||||
c.glGetShaderInfoLog(id, info_log.len, null, &info_log);
|
||||
std.log.err("shader compile error:\n{s}", .{&info_log});
|
||||
return error.ShaderCompile;
|
||||
}
|
||||
return id;
|
||||
}
|
210
scripts/wlbg/src/main.zig
Normal file
210
scripts/wlbg/src/main.zig
Normal file
|
@ -0,0 +1,210 @@
|
|||
const std = @import("std");
|
||||
const xev = @import("xev");
|
||||
const wayland = @import("wayland");
|
||||
|
||||
const c = @import("ffi.zig").c;
|
||||
|
||||
const Gfx = @import("Gfx.zig");
|
||||
const Globals = @import("Globals.zig");
|
||||
const OutputInfo = @import("OutputInfo.zig");
|
||||
|
||||
const wl = wayland.client.wl;
|
||||
const zwlr = wayland.client.zwlr;
|
||||
const zxdg = wayland.client.zxdg;
|
||||
|
||||
const fps = 10;
|
||||
|
||||
pub fn main() !void {
|
||||
std.log.info("connecting to wayland display", .{});
|
||||
const dpy = try wl.Display.connect(null);
|
||||
defer dpy.disconnect();
|
||||
const globs = try Globals.collect(dpy);
|
||||
defer globs.outputs.deinit();
|
||||
|
||||
const output_info = try std.heap.c_allocator.alloc(OutputInfo, globs.outputs.items.len);
|
||||
defer std.heap.c_allocator.free(output_info);
|
||||
@memset(output_info, .{});
|
||||
|
||||
for (globs.outputs.items, 0..) |output, i| {
|
||||
const xdg_output = try globs.xdg_output_manager.getXdgOutput(output);
|
||||
xdg_output.setListener(*OutputInfo, xdgOutputListener, &output_info[i]);
|
||||
}
|
||||
|
||||
if (dpy.roundtrip() != .SUCCESS) return error.RoundtipFail;
|
||||
|
||||
const egl_dpy = c.eglGetDisplay(@ptrCast(dpy)) orelse return error.EGLError;
|
||||
if (c.eglInitialize(egl_dpy, null, null) != c.EGL_TRUE) return error.EGLError;
|
||||
defer _ = c.eglTerminate(egl_dpy);
|
||||
|
||||
const config = egl_conf: {
|
||||
var config: c.EGLConfig = undefined;
|
||||
var n_config: i32 = 0;
|
||||
if (c.eglChooseConfig(
|
||||
egl_dpy,
|
||||
&[_]i32{
|
||||
c.EGL_SURFACE_TYPE, c.EGL_WINDOW_BIT,
|
||||
c.EGL_RENDERABLE_TYPE, c.EGL_OPENGL_ES2_BIT,
|
||||
c.EGL_RED_SIZE, 8,
|
||||
c.EGL_GREEN_SIZE, 8,
|
||||
c.EGL_BLUE_SIZE, 8,
|
||||
c.EGL_NONE,
|
||||
},
|
||||
&config,
|
||||
1,
|
||||
&n_config,
|
||||
) != c.EGL_TRUE) return error.EGLError;
|
||||
break :egl_conf config;
|
||||
};
|
||||
|
||||
std.log.info("creating EGL context", .{});
|
||||
const egl_ctx = c.eglCreateContext(
|
||||
egl_dpy,
|
||||
config,
|
||||
c.EGL_NO_CONTEXT,
|
||||
&[_]i32{
|
||||
c.EGL_CONTEXT_MAJOR_VERSION, 2,
|
||||
c.EGL_CONTEXT_OPENGL_DEBUG, 1,
|
||||
c.EGL_NONE,
|
||||
},
|
||||
) orelse return error.EGLError;
|
||||
defer _ = c.eglDestroyContext(egl_dpy, egl_ctx);
|
||||
|
||||
const output_windows = try std.heap.c_allocator.alloc(OutputWindow, globs.outputs.items.len);
|
||||
defer std.heap.c_allocator.free(output_windows);
|
||||
|
||||
for (output_windows, 0..) |*output_window, i| {
|
||||
std.log.info("creating EGL surface #{}", .{i});
|
||||
const surface = try globs.compositor.createSurface();
|
||||
|
||||
const lsurf = try globs.layer_shell.getLayerSurface(
|
||||
surface,
|
||||
globs.outputs.items[i],
|
||||
.background,
|
||||
"wlbg",
|
||||
);
|
||||
|
||||
var winsize: ?[2]c_int = null;
|
||||
lsurf.setListener(*?[2]c_int, layerSurfaceListener, &winsize);
|
||||
|
||||
lsurf.setAnchor(.{
|
||||
.top = true,
|
||||
.right = true,
|
||||
.bottom = true,
|
||||
.left = true,
|
||||
});
|
||||
lsurf.setExclusiveZone(-1);
|
||||
|
||||
surface.commit();
|
||||
|
||||
if (dpy.dispatch() != .SUCCESS) return error.DispatchFail;
|
||||
|
||||
const egl_win = win: {
|
||||
std.log.info("creating EGL window #{}", .{i});
|
||||
const size = winsize orelse return error.DidNotGetWindowSize;
|
||||
break :win try wl.EglWindow.create(surface, size[0], size[1]);
|
||||
};
|
||||
errdefer egl_win.destroy();
|
||||
|
||||
const egl_surface = c.eglCreateWindowSurface(
|
||||
egl_dpy,
|
||||
config,
|
||||
@ptrCast(egl_win),
|
||||
null,
|
||||
) orelse return error.EGLError;
|
||||
errdefer _ = c.eglDestroySurface(egl_dpy, egl_surface);
|
||||
|
||||
output_window.* = .{
|
||||
.egl_win = egl_win,
|
||||
.egl_surface = egl_surface,
|
||||
};
|
||||
}
|
||||
defer for (output_windows) |output| output.deinit(egl_ctx);
|
||||
|
||||
var total_width: i32 = 0;
|
||||
var total_height: i32 = 0;
|
||||
|
||||
for (output_info) |inf| {
|
||||
const xmax = inf.x;
|
||||
const ymax = inf.y;
|
||||
|
||||
if (xmax > total_width)
|
||||
total_width = xmax;
|
||||
if (ymax > total_height)
|
||||
total_height = ymax;
|
||||
}
|
||||
|
||||
const base_xoff = @divTrunc(total_width, 2);
|
||||
const base_yoff = @divTrunc(total_height, 2);
|
||||
|
||||
if (c.eglMakeCurrent(
|
||||
egl_dpy,
|
||||
output_windows[0].egl_surface,
|
||||
output_windows[0].egl_surface,
|
||||
egl_ctx,
|
||||
) != c.EGL_TRUE) return error.EGLError;
|
||||
|
||||
var gfx = try Gfx.init(egl_dpy);
|
||||
defer gfx.deinit();
|
||||
|
||||
var timer = try std.time.Timer.start();
|
||||
var prev_time = std.time.milliTimestamp();
|
||||
while (true) {
|
||||
const now_time = std.time.milliTimestamp();
|
||||
const delta_time: f32 = @floatFromInt(now_time - prev_time);
|
||||
prev_time = now_time;
|
||||
|
||||
for (output_windows, output_info) |output, info| {
|
||||
if (c.eglMakeCurrent(
|
||||
egl_dpy,
|
||||
output.egl_surface,
|
||||
output.egl_surface,
|
||||
egl_ctx,
|
||||
) != c.EGL_TRUE) return error.EGLError;
|
||||
try gfx.draw(delta_time, output.egl_surface, info, base_xoff, base_yoff);
|
||||
}
|
||||
|
||||
if (dpy.dispatchPending() != .SUCCESS or dpy.flush() != .SUCCESS) {
|
||||
std.log.err("error processing wayland events", .{});
|
||||
return error.DispatchFail;
|
||||
}
|
||||
|
||||
const elapsed = timer.lap();
|
||||
if (elapsed < 1000 * std.time.ns_per_ms / fps)
|
||||
std.os.nanosleep(0, (1000 * std.time.ns_per_ms) / fps - elapsed);
|
||||
}
|
||||
}
|
||||
|
||||
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| {
|
||||
winsize.* = .{ @intCast(configure.width), @intCast(configure.height) };
|
||||
lsurf.setSize(configure.width, configure.height);
|
||||
lsurf.ackConfigure(configure.serial);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
fn xdgOutputListener(_: *zxdg.OutputV1, ev: zxdg.OutputV1.Event, info: *OutputInfo) void {
|
||||
switch (ev) {
|
||||
.logical_position => |pos| {
|
||||
info.x = pos.x;
|
||||
info.y = pos.y;
|
||||
},
|
||||
.logical_size => |size| {
|
||||
info.width = size.width;
|
||||
info.height = size.height;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
390
scripts/wlbg/wlr-layer-shell-unstable-v1.xml
Normal file
390
scripts/wlbg/wlr-layer-shell-unstable-v1.xml
Normal file
|
@ -0,0 +1,390 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<protocol name="wlr_layer_shell_unstable_v1">
|
||||
<copyright>
|
||||
Copyright © 2017 Drew DeVault
|
||||
|
||||
Permission to use, copy, modify, distribute, and sell this
|
||||
software and its documentation for any purpose is hereby granted
|
||||
without fee, provided that the above copyright notice appear in
|
||||
all copies and that both that copyright notice and this permission
|
||||
notice appear in supporting documentation, and that the name of
|
||||
the copyright holders not be used in advertising or publicity
|
||||
pertaining to distribution of the software without specific,
|
||||
written prior permission. The copyright holders make no
|
||||
representations about the suitability of this software for any
|
||||
purpose. It is provided "as is" without express or implied
|
||||
warranty.
|
||||
|
||||
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
||||
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
|
||||
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
THIS SOFTWARE.
|
||||
</copyright>
|
||||
|
||||
<interface name="zwlr_layer_shell_v1" version="4">
|
||||
<description summary="create surfaces that are layers of the desktop">
|
||||
Clients can use this interface to assign the surface_layer role to
|
||||
wl_surfaces. Such surfaces are assigned to a "layer" of the output and
|
||||
rendered with a defined z-depth respective to each other. They may also be
|
||||
anchored to the edges and corners of a screen and specify input handling
|
||||
semantics. This interface should be suitable for the implementation of
|
||||
many desktop shell components, and a broad number of other applications
|
||||
that interact with the desktop.
|
||||
</description>
|
||||
|
||||
<request name="get_layer_surface">
|
||||
<description summary="create a layer_surface from a surface">
|
||||
Create a layer surface for an existing surface. This assigns the role of
|
||||
layer_surface, or raises a protocol error if another role is already
|
||||
assigned.
|
||||
|
||||
Creating a layer surface from a wl_surface which has a buffer attached
|
||||
or committed is a client error, and any attempts by a client to attach
|
||||
or manipulate a buffer prior to the first layer_surface.configure call
|
||||
must also be treated as errors.
|
||||
|
||||
After creating a layer_surface object and setting it up, the client
|
||||
must perform an initial commit without any buffer attached.
|
||||
The compositor will reply with a layer_surface.configure event.
|
||||
The client must acknowledge it and is then allowed to attach a buffer
|
||||
to map the surface.
|
||||
|
||||
You may pass NULL for output to allow the compositor to decide which
|
||||
output to use. Generally this will be the one that the user most
|
||||
recently interacted with.
|
||||
|
||||
Clients can specify a namespace that defines the purpose of the layer
|
||||
surface.
|
||||
</description>
|
||||
<arg name="id" type="new_id" interface="zwlr_layer_surface_v1"/>
|
||||
<arg name="surface" type="object" interface="wl_surface"/>
|
||||
<arg name="output" type="object" interface="wl_output" allow-null="true"/>
|
||||
<arg name="layer" type="uint" enum="layer" summary="layer to add this surface to"/>
|
||||
<arg name="namespace" type="string" summary="namespace for the layer surface"/>
|
||||
</request>
|
||||
|
||||
<enum name="error">
|
||||
<entry name="role" value="0" summary="wl_surface has another role"/>
|
||||
<entry name="invalid_layer" value="1" summary="layer value is invalid"/>
|
||||
<entry name="already_constructed" value="2" summary="wl_surface has a buffer attached or committed"/>
|
||||
</enum>
|
||||
|
||||
<enum name="layer">
|
||||
<description summary="available layers for surfaces">
|
||||
These values indicate which layers a surface can be rendered in. They
|
||||
are ordered by z depth, bottom-most first. Traditional shell surfaces
|
||||
will typically be rendered between the bottom and top layers.
|
||||
Fullscreen shell surfaces are typically rendered at the top layer.
|
||||
Multiple surfaces can share a single layer, and ordering within a
|
||||
single layer is undefined.
|
||||
</description>
|
||||
|
||||
<entry name="background" value="0"/>
|
||||
<entry name="bottom" value="1"/>
|
||||
<entry name="top" value="2"/>
|
||||
<entry name="overlay" value="3"/>
|
||||
</enum>
|
||||
|
||||
<!-- Version 3 additions -->
|
||||
|
||||
<request name="destroy" type="destructor" since="3">
|
||||
<description summary="destroy the layer_shell object">
|
||||
This request indicates that the client will not use the layer_shell
|
||||
object any more. Objects that have been created through this instance
|
||||
are not affected.
|
||||
</description>
|
||||
</request>
|
||||
</interface>
|
||||
|
||||
<interface name="zwlr_layer_surface_v1" version="4">
|
||||
<description summary="layer metadata interface">
|
||||
An interface that may be implemented by a wl_surface, for surfaces that
|
||||
are designed to be rendered as a layer of a stacked desktop-like
|
||||
environment.
|
||||
|
||||
Layer surface state (layer, size, anchor, exclusive zone,
|
||||
margin, interactivity) is double-buffered, and will be applied at the
|
||||
time wl_surface.commit of the corresponding wl_surface is called.
|
||||
|
||||
Attaching a null buffer to a layer surface unmaps it.
|
||||
|
||||
Unmapping a layer_surface means that the surface cannot be shown by the
|
||||
compositor until it is explicitly mapped again. The layer_surface
|
||||
returns to the state it had right after layer_shell.get_layer_surface.
|
||||
The client can re-map the surface by performing a commit without any
|
||||
buffer attached, waiting for a configure event and handling it as usual.
|
||||
</description>
|
||||
|
||||
<request name="set_size">
|
||||
<description summary="sets the size of the surface">
|
||||
Sets the size of the surface in surface-local coordinates. The
|
||||
compositor will display the surface centered with respect to its
|
||||
anchors.
|
||||
|
||||
If you pass 0 for either value, the compositor will assign it and
|
||||
inform you of the assignment in the configure event. You must set your
|
||||
anchor to opposite edges in the dimensions you omit; not doing so is a
|
||||
protocol error. Both values are 0 by default.
|
||||
|
||||
Size is double-buffered, see wl_surface.commit.
|
||||
</description>
|
||||
<arg name="width" type="uint"/>
|
||||
<arg name="height" type="uint"/>
|
||||
</request>
|
||||
|
||||
<request name="set_anchor">
|
||||
<description summary="configures the anchor point of the surface">
|
||||
Requests that the compositor anchor the surface to the specified edges
|
||||
and corners. If two orthogonal edges are specified (e.g. 'top' and
|
||||
'left'), then the anchor point will be the intersection of the edges
|
||||
(e.g. the top left corner of the output); otherwise the anchor point
|
||||
will be centered on that edge, or in the center if none is specified.
|
||||
|
||||
Anchor is double-buffered, see wl_surface.commit.
|
||||
</description>
|
||||
<arg name="anchor" type="uint" enum="anchor"/>
|
||||
</request>
|
||||
|
||||
<request name="set_exclusive_zone">
|
||||
<description summary="configures the exclusive geometry of this surface">
|
||||
Requests that the compositor avoids occluding an area with other
|
||||
surfaces. The compositor's use of this information is
|
||||
implementation-dependent - do not assume that this region will not
|
||||
actually be occluded.
|
||||
|
||||
A positive value is only meaningful if the surface is anchored to one
|
||||
edge or an edge and both perpendicular edges. If the surface is not
|
||||
anchored, anchored to only two perpendicular edges (a corner), anchored
|
||||
to only two parallel edges or anchored to all edges, a positive value
|
||||
will be treated the same as zero.
|
||||
|
||||
A positive zone is the distance from the edge in surface-local
|
||||
coordinates to consider exclusive.
|
||||
|
||||
Surfaces that do not wish to have an exclusive zone may instead specify
|
||||
how they should interact with surfaces that do. If set to zero, the
|
||||
surface indicates that it would like to be moved to avoid occluding
|
||||
surfaces with a positive exclusive zone. If set to -1, the surface
|
||||
indicates that it would not like to be moved to accommodate for other
|
||||
surfaces, and the compositor should extend it all the way to the edges
|
||||
it is anchored to.
|
||||
|
||||
For example, a panel might set its exclusive zone to 10, so that
|
||||
maximized shell surfaces are not shown on top of it. A notification
|
||||
might set its exclusive zone to 0, so that it is moved to avoid
|
||||
occluding the panel, but shell surfaces are shown underneath it. A
|
||||
wallpaper or lock screen might set their exclusive zone to -1, so that
|
||||
they stretch below or over the panel.
|
||||
|
||||
The default value is 0.
|
||||
|
||||
Exclusive zone is double-buffered, see wl_surface.commit.
|
||||
</description>
|
||||
<arg name="zone" type="int"/>
|
||||
</request>
|
||||
|
||||
<request name="set_margin">
|
||||
<description summary="sets a margin from the anchor point">
|
||||
Requests that the surface be placed some distance away from the anchor
|
||||
point on the output, in surface-local coordinates. Setting this value
|
||||
for edges you are not anchored to has no effect.
|
||||
|
||||
The exclusive zone includes the margin.
|
||||
|
||||
Margin is double-buffered, see wl_surface.commit.
|
||||
</description>
|
||||
<arg name="top" type="int"/>
|
||||
<arg name="right" type="int"/>
|
||||
<arg name="bottom" type="int"/>
|
||||
<arg name="left" type="int"/>
|
||||
</request>
|
||||
|
||||
<enum name="keyboard_interactivity">
|
||||
<description summary="types of keyboard interaction possible for a layer shell surface">
|
||||
Types of keyboard interaction possible for layer shell surfaces. The
|
||||
rationale for this is twofold: (1) some applications are not interested
|
||||
in keyboard events and not allowing them to be focused can improve the
|
||||
desktop experience; (2) some applications will want to take exclusive
|
||||
keyboard focus.
|
||||
</description>
|
||||
|
||||
<entry name="none" value="0">
|
||||
<description summary="no keyboard focus is possible">
|
||||
This value indicates that this surface is not interested in keyboard
|
||||
events and the compositor should never assign it the keyboard focus.
|
||||
|
||||
This is the default value, set for newly created layer shell surfaces.
|
||||
|
||||
This is useful for e.g. desktop widgets that display information or
|
||||
only have interaction with non-keyboard input devices.
|
||||
</description>
|
||||
</entry>
|
||||
<entry name="exclusive" value="1">
|
||||
<description summary="request exclusive keyboard focus">
|
||||
Request exclusive keyboard focus if this surface is above the shell surface layer.
|
||||
|
||||
For the top and overlay layers, the seat will always give
|
||||
exclusive keyboard focus to the top-most layer which has keyboard
|
||||
interactivity set to exclusive. If this layer contains multiple
|
||||
surfaces with keyboard interactivity set to exclusive, the compositor
|
||||
determines the one receiving keyboard events in an implementation-
|
||||
defined manner. In this case, no guarantee is made when this surface
|
||||
will receive keyboard focus (if ever).
|
||||
|
||||
For the bottom and background layers, the compositor is allowed to use
|
||||
normal focus semantics.
|
||||
|
||||
This setting is mainly intended for applications that need to ensure
|
||||
they receive all keyboard events, such as a lock screen or a password
|
||||
prompt.
|
||||
</description>
|
||||
</entry>
|
||||
<entry name="on_demand" value="2" since="4">
|
||||
<description summary="request regular keyboard focus semantics">
|
||||
This requests the compositor to allow this surface to be focused and
|
||||
unfocused by the user in an implementation-defined manner. The user
|
||||
should be able to unfocus this surface even regardless of the layer
|
||||
it is on.
|
||||
|
||||
Typically, the compositor will want to use its normal mechanism to
|
||||
manage keyboard focus between layer shell surfaces with this setting
|
||||
and regular toplevels on the desktop layer (e.g. click to focus).
|
||||
Nevertheless, it is possible for a compositor to require a special
|
||||
interaction to focus or unfocus layer shell surfaces (e.g. requiring
|
||||
a click even if focus follows the mouse normally, or providing a
|
||||
keybinding to switch focus between layers).
|
||||
|
||||
This setting is mainly intended for desktop shell components (e.g.
|
||||
panels) that allow keyboard interaction. Using this option can allow
|
||||
implementing a desktop shell that can be fully usable without the
|
||||
mouse.
|
||||
</description>
|
||||
</entry>
|
||||
</enum>
|
||||
|
||||
<request name="set_keyboard_interactivity">
|
||||
<description summary="requests keyboard events">
|
||||
Set how keyboard events are delivered to this surface. By default,
|
||||
layer shell surfaces do not receive keyboard events; this request can
|
||||
be used to change this.
|
||||
|
||||
This setting is inherited by child surfaces set by the get_popup
|
||||
request.
|
||||
|
||||
Layer surfaces receive pointer, touch, and tablet events normally. If
|
||||
you do not want to receive them, set the input region on your surface
|
||||
to an empty region.
|
||||
|
||||
Keyboard interactivity is double-buffered, see wl_surface.commit.
|
||||
</description>
|
||||
<arg name="keyboard_interactivity" type="uint" enum="keyboard_interactivity"/>
|
||||
</request>
|
||||
|
||||
<request name="get_popup">
|
||||
<description summary="assign this layer_surface as an xdg_popup parent">
|
||||
This assigns an xdg_popup's parent to this layer_surface. This popup
|
||||
should have been created via xdg_surface::get_popup with the parent set
|
||||
to NULL, and this request must be invoked before committing the popup's
|
||||
initial state.
|
||||
|
||||
See the documentation of xdg_popup for more details about what an
|
||||
xdg_popup is and how it is used.
|
||||
</description>
|
||||
<arg name="popup" type="object" interface="xdg_popup"/>
|
||||
</request>
|
||||
|
||||
<request name="ack_configure">
|
||||
<description summary="ack a configure event">
|
||||
When a configure event is received, if a client commits the
|
||||
surface in response to the configure event, then the client
|
||||
must make an ack_configure request sometime before the commit
|
||||
request, passing along the serial of the configure event.
|
||||
|
||||
If the client receives multiple configure events before it
|
||||
can respond to one, it only has to ack the last configure event.
|
||||
|
||||
A client is not required to commit immediately after sending
|
||||
an ack_configure request - it may even ack_configure several times
|
||||
before its next surface commit.
|
||||
|
||||
A client may send multiple ack_configure requests before committing, but
|
||||
only the last request sent before a commit indicates which configure
|
||||
event the client really is responding to.
|
||||
</description>
|
||||
<arg name="serial" type="uint" summary="the serial from the configure event"/>
|
||||
</request>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="destroy the layer_surface">
|
||||
This request destroys the layer surface.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<event name="configure">
|
||||
<description summary="suggest a surface change">
|
||||
The configure event asks the client to resize its surface.
|
||||
|
||||
Clients should arrange their surface for the new states, and then send
|
||||
an ack_configure request with the serial sent in this configure event at
|
||||
some point before committing the new surface.
|
||||
|
||||
The client is free to dismiss all but the last configure event it
|
||||
received.
|
||||
|
||||
The width and height arguments specify the size of the window in
|
||||
surface-local coordinates.
|
||||
|
||||
The size is a hint, in the sense that the client is free to ignore it if
|
||||
it doesn't resize, pick a smaller size (to satisfy aspect ratio or
|
||||
resize in steps of NxM pixels). If the client picks a smaller size and
|
||||
is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the
|
||||
surface will be centered on this axis.
|
||||
|
||||
If the width or height arguments are zero, it means the client should
|
||||
decide its own window dimension.
|
||||
</description>
|
||||
<arg name="serial" type="uint"/>
|
||||
<arg name="width" type="uint"/>
|
||||
<arg name="height" type="uint"/>
|
||||
</event>
|
||||
|
||||
<event name="closed">
|
||||
<description summary="surface should be closed">
|
||||
The closed event is sent by the compositor when the surface will no
|
||||
longer be shown. The output may have been destroyed or the user may
|
||||
have asked for it to be removed. Further changes to the surface will be
|
||||
ignored. The client should destroy the resource after receiving this
|
||||
event, and create a new surface if they so choose.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<enum name="error">
|
||||
<entry name="invalid_surface_state" value="0" summary="provided surface state is invalid"/>
|
||||
<entry name="invalid_size" value="1" summary="size is invalid"/>
|
||||
<entry name="invalid_anchor" value="2" summary="anchor bitfield is invalid"/>
|
||||
<entry name="invalid_keyboard_interactivity" value="3" summary="keyboard interactivity is invalid"/>
|
||||
</enum>
|
||||
|
||||
<enum name="anchor" bitfield="true">
|
||||
<entry name="top" value="1" summary="the top edge of the anchor rectangle"/>
|
||||
<entry name="bottom" value="2" summary="the bottom edge of the anchor rectangle"/>
|
||||
<entry name="left" value="4" summary="the left edge of the anchor rectangle"/>
|
||||
<entry name="right" value="8" summary="the right edge of the anchor rectangle"/>
|
||||
</enum>
|
||||
|
||||
<!-- Version 2 additions -->
|
||||
|
||||
<request name="set_layer" since="2">
|
||||
<description summary="change the layer of the surface">
|
||||
Change the layer that the surface is rendered on.
|
||||
|
||||
Layer is double-buffered, see wl_surface.commit.
|
||||
</description>
|
||||
<arg name="layer" type="uint" enum="zwlr_layer_shell_v1.layer" summary="layer to move this surface to"/>
|
||||
</request>
|
||||
</interface>
|
||||
</protocol>
|
|
@ -31,6 +31,7 @@
|
|||
(install-zig "scripts/randomwallpaper")
|
||||
(install-zig "scripts/vinput")
|
||||
(install-zig "scripts/withjava")
|
||||
(install-zig "scripts/wlbg")
|
||||
|
||||
(install-roswell "scripts/launchmenu.ros")
|
||||
(install-roswell "scripts/playvid.ros")
|
||||
|
|
Loading…
Reference in a new issue