A framework for building waywall configurations for Minecraft speedrunning setups on Linux/Wayland.
Waywork provides a structured, modular approach to managing waywall configurations. It abstracts common patterns like resolution switching, scene management, and process orchestration into reusable components, making waywall configs more maintainable and easier to understand.
For reference you can look at my config which uses waywork here: Esensats/waywall-config
Clone the repository into your waywall configuration directory:
# For example:
git clone --depth 1 https://github.com/Esensats/waywork ~/.config/waywall/wayworkManages visual elements (mirrors, images, text) uniformly with grouping and lifecycle management.
Features:
- Unified Management: Handle mirrors, images, and text objects through a single interface
- Grouping: Organize scene objects into logical groups for batch operations
- Lazy Loading: Objects are only created when enabled
- Dynamic Updates: Modify object properties at runtime
Example:
local Scene = require("waywork.scene")
local scene = Scene.SceneManager.new(waywall)
-- Register scene objects
scene:register("e_counter", {
kind = "mirror",
options = {
src = { x = 1, y = 37, w = 49, h = 9 },
dst = { x = 1150, y = 300, w = 196, h = 36 }
},
groups = { "thin" },
})
scene:register("eye_overlay", {
kind = "image",
path = "/path/to/overlay.png",
options = { dst = { x = 30, y = 340, w = 700, h = 400 } },
groups = { "tall" },
})
-- Enable/disable by group
scene:enable_group("thin", true) -- Enable all "thin" objects
scene:enable_group("tall", false) -- Disable all "tall" objects
-- Enable/disable individual objects
scene:enable("e_counter", true)Orchestrates resolution switching with enter/exit hooks and guard conditions.
Features:
- Resolution Management: Automatic resolution switching with cleanup
- Lifecycle Hooks:
on_enterandon_exitcallbacks for mode transitions - Toggle Guards: Conditional guards to prevent accidental mode switches (e.g. pressing F3 + F4 to switch gamemode, but accidentally triggering mode transition instead)
- State Tracking: Knows which mode is currently active
Example:
local Modes = require("waywork.modes")
local ModeManager = Modes.ModeManager.new(waywall)
ModeManager:define("thin", {
width = 340,
height = 1080,
on_enter = function()
scene:enable_group("thin", true)
end,
on_exit = function()
scene:enable_group("thin", false)
end,
})
ModeManager:define("tall", {
width = 384,
height = 16384,
toggle_guard = function()
return not waywall.get_key("F3") -- Prevent toggle during F3 debug
end,
on_enter = function()
scene:enable_group("tall", true)
waywall.set_sensitivity(tall_sens)
end,
on_exit = function()
scene:enable_group("tall", false)
waywall.set_sensitivity(0)
end,
})
-- Toggle modes
ModeManager:toggle("thin") -- Switch to thin mode
ModeManager:toggle("thin") -- Switch back to default (0x0)Simple utility for building action tables from key mappings.
Example:
local Keys = require("waywork.keys")
local actions = Keys.actions({
["*-Alt_L"] = function()
return ModeManager:toggle("thin")
end,
["*-F4"] = function()
return ModeManager:toggle("tall")
end,
["Ctrl-E"] = function()
waywall.press_key("ESC")
end,
})
config.actions = actionsLow-level utilities used throughout the framework.
Features:
- Toggle: Boolean state management with callbacks
- Resettable Timeout: Timeout that cancels previous invocations
- Table Operations: Copy and merge utilities
Utilities for managing external processes with shell command handling.
Features:
- Process Detection: Check if processes are running using
pgrep - Java JAR Support: Specialized utilities for launching Java applications
- Argument Handling: Proper handling of command arguments as arrays
Example:
local P = require("waywork.processes")
-- Check if a process is running
if P.is_running("firefox") then
print("Firefox is running")
end
-- Create Java JAR launchers with proper argument handling
local ensure_paceman = P.ensure_java_jar(
waywall,
"/usr/lib/jvm/java-24-openjdk/bin/java",
"/home/user/apps/paceman-tracker.jar",
{"--nogui"} -- arguments as array
)("paceman-tracker\\.jar*") -- process pattern to check
local ensure_ninjabrain = P.ensure_java_jar(
waywall,
"/usr/lib/jvm/java-24-openjdk/bin/java",
"/home/user/apps/ninjabrain-bot.jar",
{"-Dawt.useSystemAAFontSettings=on"} -- JVM arguments
)("ninjabrain-bot\\.jar") -- process pattern to check
-- Use in key bindings
["Ctrl-Shift-P"] = function()
ensure_ninjabrain() -- ensure Ninjabrain is running
ensure_paceman() -- ensure Paceman is running
end,-- Scattered mirror management
local make_mirror = function(options)
local this = nil
return function(enable)
if enable and not this then
this = waywall.mirror(options)
elseif this and not enable then
this:close()
this = nil
end
end
end
local mirrors = {
e_counter = make_mirror({ src = {...}, dst = {...} }),
thin_pie_all = make_mirror({ src = {...}, dst = {...} }),
-- ... dozens more
}
-- Manual resolution management
local make_res = function(width, height, enable, disable)
return function()
local active_width, active_height = waywall.active_res()
if active_width == width and active_height == height then
waywall.set_resolution(0, 0)
disable()
else
waywall.set_resolution(width, height)
enable()
end
end
endlocal waywall = require("waywall")
local Scene = require("waywork.scene")
local Modes = require("waywork.modes")
local Keys = require("waywork.keys")
local scene = Scene.SceneManager.new(waywall)
local ModeManager = Modes.ModeManager.new(waywall)
-- Clean object registration
scene:register("e_counter", {
kind = "mirror",
options = { src = {...}, dst = {...} },
groups = { "thin" },
})
-- Declarative mode definitions
ModeManager:define("thin", {
width = 340,
height = 1080,
on_enter = function() scene:enable_group("thin", true) end,
on_exit = function() scene:enable_group("thin", false) end,
})
-- Simple key mappings
local actions = Keys.actions({
["*-Alt_L"] = function() return ModeManager:toggle("thin") end,
})- Reduced Boilerplate: Framework handles object lifecycle, resolution management, and state tracking
- Better Organization: Logical grouping of related functionality
- Maintainability: Clear separation of concerns and declarative configuration
- Reusability: Common patterns abstracted into reusable components
- Error Prevention: Toggle guards and proper state management prevent common issues
- Cleaner Code: Focus on what you want to achieve, not how to implement it