diff --git a/scripts/wlbg/.gitignore b/scripts/wlbg/.gitignore new file mode 100644 index 0000000..fe95f8d --- /dev/null +++ b/scripts/wlbg/.gitignore @@ -0,0 +1 @@ +/zig-* diff --git a/scripts/wlbg/build.zig b/scripts/wlbg/build.zig new file mode 100644 index 0000000..d729927 --- /dev/null +++ b/scripts/wlbg/build.zig @@ -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); +} diff --git a/scripts/wlbg/build.zig.zon b/scripts/wlbg/build.zig.zon new file mode 100644 index 0000000..27e219c --- /dev/null +++ b/scripts/wlbg/build.zig.zon @@ -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", + }, + }, +} diff --git a/scripts/wlbg/src/Gfx.zig b/scripts/wlbg/src/Gfx.zig new file mode 100644 index 0000000..70e49b1 --- /dev/null +++ b/scripts/wlbg/src/Gfx.zig @@ -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; +} diff --git a/scripts/wlbg/src/Globals.zig b/scripts/wlbg/src/Globals.zig new file mode 100644 index 0000000..2381884 --- /dev/null +++ b/scripts/wlbg/src/Globals.zig @@ -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"); +} diff --git a/scripts/wlbg/src/OutputInfo.zig b/scripts/wlbg/src/OutputInfo.zig new file mode 100644 index 0000000..2b12925 --- /dev/null +++ b/scripts/wlbg/src/OutputInfo.zig @@ -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, diff --git a/scripts/wlbg/src/bg_frag.glsl b/scripts/wlbg/src/bg_frag.glsl new file mode 100644 index 0000000..3b592db --- /dev/null +++ b/scripts/wlbg/src/bg_frag.glsl @@ -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; +} + diff --git a/scripts/wlbg/src/bg_vert.glsl b/scripts/wlbg/src/bg_vert.glsl new file mode 100644 index 0000000..f7a079c --- /dev/null +++ b/scripts/wlbg/src/bg_vert.glsl @@ -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; +} diff --git a/scripts/wlbg/src/ffi.zig b/scripts/wlbg/src/ffi.zig new file mode 100644 index 0000000..8a15957 --- /dev/null +++ b/scripts/wlbg/src/ffi.zig @@ -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"); +}); diff --git a/scripts/wlbg/src/glutil.zig b/scripts/wlbg/src/glutil.zig new file mode 100644 index 0000000..b6a82f5 --- /dev/null +++ b/scripts/wlbg/src/glutil.zig @@ -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; +} diff --git a/scripts/wlbg/src/main.zig b/scripts/wlbg/src/main.zig new file mode 100644 index 0000000..81901e0 --- /dev/null +++ b/scripts/wlbg/src/main.zig @@ -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 => {}, + } +} diff --git a/scripts/wlbg/wlr-layer-shell-unstable-v1.xml b/scripts/wlbg/wlr-layer-shell-unstable-v1.xml new file mode 100644 index 0000000..d62fd51 --- /dev/null +++ b/scripts/wlbg/wlr-layer-shell-unstable-v1.xml @@ -0,0 +1,390 @@ + + + + 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. + + + + + 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. + + + + + 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. + + + + + + + + + + + + + + + + + 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. + + + + + + + + + + + + + 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. + + + + + + + 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. + + + + + 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. + + + + + + + + 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. + + + + + + + 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. + + + + + + + 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. + + + + + + + + + + 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. + + + + + 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. + + + + + 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. + + + + + 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. + + + + + + + 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. + + + + + + + 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. + + + + + + + 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. + + + + + + + This request destroys the layer surface. + + + + + + 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. + + + + + + + + + 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. + + + + + + + + + + + + + + + + + + + + + + Change the layer that the surface is rendered on. + + Layer is double-buffered, see wl_surface.commit. + + + + + diff --git a/setup/commands/install-scripts.rkt b/setup/commands/install-scripts.rkt index 05e13ec..9a1cf45 100644 --- a/setup/commands/install-scripts.rkt +++ b/setup/commands/install-scripts.rkt @@ -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")