waywall Discord

waywall is a Wayland compositor that provides various convenient features (key rebinding, Ninjabrain Bot support, etc) for Minecraft speedrunning. It is designed to be nested within an existing Wayland session and is intended as a successor to resetti.

Installation

Currently, there are no distribution packages for waywall, so compiling from source is the only method of installation.

Building from source

waywall is written in C and uses the Meson build system, so you will need to install a C toolchain and meson if they are not already on your system.

You will also need to install the following dependencies from your distribution's package repositories:

  • egl
  • glesv2
  • luajit
  • spng
  • wayland-client
  • wayland-cursor
  • wayland-egl
  • wayland-protocols
  • wayland-server
  • xcb
  • xcb-composite
  • xcb-res
  • xcb-xtest
  • xwayland
  • xkbcommon

Many distributions, such as Fedora and Debian, split the "development files" (e.g. pkg-config data and C headers) into separate -dev or -devel packages. Make sure to find and install these in addition to the normal versions.

Compiling

You can download and build a copy of waywall with the following commands:

git clone https://github.com/tesselslate/waywall
cd waywall
make

The compiled binary will be located at build/waywall/waywall. If you'd like, you can move it to somewhere on your $PATH.

Setup

After compiling waywall, you will need to configure your instance(s) to use it. Prism Launcher is the best choice, although any variant of MultiMC should work.

GLFW

Minecraft uses a library known as GLFW in order to create a window and receive keyboard and mouse input. Unfortunately, the version shipped by default does not work with waywall, so you will need to compile a patched version of GLFW.

You can compile the patched version of GLFW with the following commands:

# clone GLFW
git clone https://github.com/glfw/glfw
cd glfw
git checkout 3.4

# apply the patches
curl -o glfw.patch https://raw.githubusercontent.com/tesselslate/waywall/be3e018bb5f7c25610da73cc320233a26dfce948/contrib/glfw.patch
git apply glfw.patch

# compile GLFW
cmake -S . -B build -DBUILD_SHARED_LIBS=ON -DGLFW_BUILD_WAYLAND=ON
cd build
make

After running these commands, the new patched version of GLFW will be located at glfw/build/src/libglfw.so (or src/libglfw.so from the build directory.) You can copy it to a safer location like ~/.local/lib64.

Instance setup

First, configure your instance to use the patched version of GLFW by opening its settings (with the Edit button on the right pane) and going to Settings -> Workarounds. Then, enable Native libraries and Use system installation of GLFW. Finally, enter the path to the patched libglfw.so you just compiled.

The Prism Launcher menu for enabling patched GLFW

Make sure you configure the instance to use patched GLFW correctly! If the instance still uses the default version of GLFW, waywall will not do anything when the game launches, since the game will still be running under Xwayland.


Next, you need to configure your instance to use waywall. Navigate to the Custom commands submenu and enter waywall wrap -- into the Wrapper command textbox. If needed, change waywall to point to the waywall executable you compiled earlier.

The Prism Launcher menu for using waywall

Configuration

waywall will not start up without a configuration file. It will search for one in $XDG_CONFIG_HOME/waywall (typically ~/.config/waywall/). You can create a file with the following contents at ~/.config/waywall/init.lua as a starting point:

local waywall = require("waywall")
local helpers = require("waywall.helpers")

local config = {
    input = {
        layout = "us",
        repeat_rate = 40,
        repeat_delay = 300,

        sensitivity = 1.0,
        confine_pointer = false,
    },
    theme = {
        background = "#303030ff",
    },
}

config.actions = {}

return config

NVIDIA

If you use an NVIDIA GPU, you will also need to set the environment variable __GL_THREADED_OPTIMIZATIONS to 0. This can be done in the Environment variables submenu.

This environment variable fixes a startup crash (GLFW error 65544) and also ensures that preemptive navigation works correctly.

Lua

waywall is configured using the Lua programming language. If you have not used Lua before, you may find the documentation to be helpful:

If you are familiar with Lua, it is worth noting that waywall uses LuaJIT and makes some small changes to the behavior and standard library of Lua. See Lua changes for more information.

Lua code executed by waywall is allowed to interact with the host operating system in various ways, such as spawning subprocesses and modifying files on disk. Read other people's code and do not blindly copy paste it into your own configuration.

Profiles

By default, waywall attempts to read and execute a configuration file from $XDG_CONFIG_HOME/waywall/init.lua (typically, this is equivalent to ~/.config/waywall/init.lua).

If launched with a --profile foo argument, waywall will instead attempt to read and execute a configuration file from $XDG_CONFIG_HOME/waywall/foo.lua. Any number of profiles can exist within the waywall configuration directory.

Hot reload

waywall attempts to watch for any and all changes to .lua files within the waywall configuration directory. When it detects a change, it will attempt to automatically reload your configuration. If successful, the Lua VM will be recreated and any changes to variables or other state within will not be transferred to the new configuration.

Options

waywall has a number of built-in options for configuring its appearance and behavior. These options can be set by returning a table from your configuration file, e.g.:

local config = {
    input = {
        layout = "us",

        -- ... more options here
    },

    -- ... more options here
}

return config

The following sections will cover all of the available options which can be set using this configuration table.

Actions

The actions section of the configuration table allows you to configure any number of "actions," which are arbitrary Lua functions that get executed when a key or button combo is pressed.

Default values

local config = {
    actions = {},
}

return config

Configuration

The actions table should contain a list of key-value pairs where each key is a string describing the input (containing a key or button and any number of modifiers) and the value is a function to be executed when the input is received. For example:

local config = {
    actions = {
        -- This will run if you press T with no modifier keys held.
        ["T"] = function() end,

        -- This will run if you press T with only Shift held.
        ["Shift-T"] = function() end,

        -- This will run if you press Button 4 (side button) with only Control
        -- held.
        ["Ctrl-MB4"] = function() end,
    },
}

return config

The full lists of keysyms, mouse buttons, and modifiers can be found in the Lookup Tables section.

An action will only trigger if your pressed modifiers exactly match those specified. For example, an action for "T" will only trigger if you press T while Shift, Control, etc. are not pressed.

The * modifier acts as a wildcard, allowing the action to run while other modifier keys are pressed. An action for "*-T" will trigger if you press T, regardless of what modifier keys (Shift, Control, etc.) are pressed.

This also works in combination with other modifiers: "*-Shift-T" will trigger as long as Shift is pressed, but other modifiers will not prevent the action from being run.

Input consumption

By default, if waywall finds and runs an action, the input which triggered the action will be silently dropped and not passed on to the Minecraft instance.

However, in some cases (e.g. if an action fails to run), you may want the input to be passed along to the Minecraft instance as normal. You can make this happen by returning false from your action's function.

If your action pauses execution at any point (e.g. by calling waywall.sleep()) it will always consume the input, even if you return false.

Input

The input section of the configuration table allows you to configure how keyboard and mouse input is processed by waywall in a number of ways.

Default values

local config = {
    input = {
        -- XKB keymap options
        layout = "",
        model = "",
        rules = "",
        variant = "",
        options = "",

        -- key/button remappings
        remaps = {},

        -- key repeat
        repeat_rate = -1,
        repeat_delay = -1,

        -- mouse options
        sensitivity = 1.0,
        confine_pointer = false,
    },
}

return config

Keymap configuration

waywall allows you to use a different keyboard layout from the rest of your Wayland session (e.g. for search crafting with different languages).

There are five options in the input table which can be used to configure which keymap is loaded by XKB:

layout, model, rules, variant, options

Unfortunately, detailed information on XKB configuration is outside the scope of this document. You can refer to the libxkbcommon documentation for more information. If installed, you can also refer to the standard database of XKB configuration rules, which is typically located at /usr/share/X11/xkb.

Input remapping

waywall allows you to remap keys and mouse buttons to one another in any fashion via the remaps table within the input table. Here are a few examples:

{
    ["MB4"] = "Home",   -- remap side button to Home
    ["X"] = "F3",       -- remap X to F3
}

Each key-value pair specifies a single remapping, where the key is the source input (button or key) and the value is the resulting output which gets sent to the game.

The list of all available keycodes and mouse buttons can be found in the Lookup Tables section.

Key repeat

waywall allows you to configure how key repeat works with the repeat_rate and repeat_delay options.

The repeat_rate option specifies how many times per second a keypress should be repeated while it is held down. This option is mostly useful for increasing the speed at which the F3+F key combination changes your render distance.

The repeat_delay option determines how long (in milliseconds) a key must be held down before it will start repeating.

By default, both options are set to a value of -1, which means they will use the same values as your main Wayland session.

If you use a short repeat_delay and set repeat_rate to more than 20 Hz, you may experience issues with switching hotbar slots. This is due to a bug in how the game handles certain keybinds and can be avoided by either using a longer repeat_delay or using a repeat_rate no greater than 20 Hz.

Mouse sensitivity

The sensitivity option applies a constant multiplier to your mouse motion while aiming with the camera ingame. The default value of 1.0 results in no change, while 2.0 would make it twice as fast and 0.5 would make it half as fast.

This option only affects your camera movement, not your mouse movement in menus. Additionally, it only takes effect when Raw Input is disabled ingame.

Pointer confinement

The confine_pointer option allows you to lock your mouse pointer to the waywall window. This can be useful if you have two monitors and accidentally flick your mouse to the side while in your inventory, which might otherwise cause you to focus a different window and lose time.

Theme

The theme section of the configuration table allows you to configure the appearance of waywall.

Default values

local config = {
    theme = {
        background = "#000000ff",

        cursor_theme = "",
        cursor_icon = "",
        cursor_size = 0,

        ninb_anchor = "",
        ninb_opacity = 1.0,
    },
}

return config

Background

The background option determines the color of the background of the waywall window. The background is only visible while the Minecraft window does not occupy the entire waywall window (e.g. while using Thin BT or boat eye.)

You may specify either an RGB or RGBA hex color. Different compositors may handle non-opaque background colors differently, and non-opaque colors may not appear correctly if waywall is fullscreened.

Cursor theme

waywall provides three options for configuring the appearance of the cursor:

cursor_theme, cursor_icon, cursor_size

By default, these options are left unset, and waywall will attempt to automatically detect and use the cursor settings of your main Wayland session.

The cursor_theme option should contain the name of an installed icon theme (typically in /usr/share/icons or ~/.local/share/icons). The cursor_icon option should point to a valid Xcursor file within the specified theme.

On some compositors, such as mutter (GNOME), waywall's automatic cursor theme detection will fail. waywall attempts to use the XCURSOR_* environment variables and cursor_shape_v1 protocol for picking a cursor theme, but GNOME does not support either of these mechanisms and instead only exposes a GNOME-specific DBus interface.

Ninjabrain Bot

There are two options for changing the appearance of Ninjabrain Bot:

ninb_anchor, ninb_opacity

If set, the ninb_anchor option will cause the Ninjabrain Bot window to be locked to a specific side or corner of the waywall window. The following are valid values for ninb_anchor:

  • topleft
  • top
  • topright
  • left
  • right
  • bottomleft
  • bottomright

The ninb_opacity option allows you to make the Ninjabrain Bot window translucent. The default value of 1.0 results in a fully opaque window, while values between 0.0 and 1.0 will result in varying degrees of translucency.

The ninb_opacity option requires that your compositor supports the alpha_modifier_v1 protocol. If it is not supported, the option will have no effect.

Shaders

The shaders section of the configuration table allows you to load custom OpenGL shaders to further customize the appearance of various scene objects (mirrors, images, text).

This option is intended for more advanced users who are able to write shaders to do what they want. A tutorial on how to write OpenGL shaders is outside of the scope of this documentation (and I am not particularly qualified to write one anyway.)

Default values

local config = {
    shaders = {},
}

return config

Configuration

The shaders table should contain a list of key-value pairs where each key is a string containing the name of the shader and the value is a table describing the shader's sources (fragment and/or vertex). For example:

local read_file = function(name)
    local home = os.getenv("HOME")

    local file = io.open(home .. "/.config/waywall/" .. name, "r")
    local data = file:read("*a")
    file:close()

    return data
end

local config = {
    shaders = {
        ["pie_chart"] = {
            vertex = read_file("pie_chart.vert"),
            fragment = read_file("pie_chart.frag"),
        }
    }
}

return config

If either vertex or fragment is unspecified, they will default to the implementations provided by waywall's built-in "texcopy" shader. You can view the sources for the texcopy shader here.

Vertex format

The following attributes are provided to the vertex shader. You can use as many as you like, and leaving attributes unused is fine.

v_src_pos (vec2)

v_src_pos contains the pixel coordinate which should be sampled from the source texture.

v_dst_pos (vec2)

v_dst_pos contains the pixel coordinate at which the vertex is located.

v_src_rgba (vec4)

v_src_rgba contains the source color for the color-keying operation (if any). An alpha value of 0 signals that no color keying should happen.

v_dst_rgba (vec4)

v_dst_rgba contains the output color for the color-keying operation (if any).

Uniforms

The following uniforms are provided to the vertex shader:

u_src_size (vec2)

u_src_size contains the size of the source texture in pixels.

u_dst_size (vec2)

u_dst_size contains the size of the destination texture (waywall window) in pixels.

Example

The following shaders perform color-keying to only accept the three main colors of the gameRenderer pie chart (orange for block entities, pink for entities, and green for unspecified):

pie_chart.vert

attribute vec2 v_src_pos;
attribute vec2 v_dst_pos;

uniform vec2 u_src_size;
uniform vec2 u_dst_size;

varying vec2 f_src_pos;

void main() {
    gl_Position.x = 2.0 * (v_dst_pos.x / u_dst_size.x) - 1.0;
    gl_Position.y = 1.0 - 2.0 * (v_dst_pos.y / u_dst_size.y);
    gl_Position.zw = vec2(1.0);

    f_src_pos = v_src_pos / u_src_size;
}

pie_chart.frag

precision highp float;

varying vec2 f_src_pos;

uniform sampler2D u_texture;

const float threshold = 0.01;
const vec3 pink = vec3(0.882, 0.271, 0.761); // #e145c2
const vec3 orange = vec3(0.914, 0.427, 0.302); // #e96d4d
const vec3 green = vec3(0.271, 0.796, 0.396); // #45cb65

void main() {
    vec4 color = texture2D(u_texture, f_src_pos);

    bool is_pink = all(lessThan(abs(color.rgb - pink), vec3(threshold)));
    bool is_orange = all(lessThan(abs(color.rgb - orange), vec3(threshold)));
    bool is_green = all(lessThan(abs(color.rgb - green), vec3(threshold)));

    if (is_pink || is_orange || is_green) {
        gl_FragColor = color;
    } else {
        gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
    }
}

Experimental

The experimental section of the configuration table contains miscellaneous and developer-focused settings.

Default values

local config = {
    experimental = {
        debug = false,
        jit = false,
        tearing = false,
    },
}

return config

Debug

Debug text

When enabled, the debug option will draw text about the state of waywall in the upper left corner of the window.

This information is usually only needed for development purposes.

JIT

waywall uses LuaJIT as its Lua implementation. By default, the JIT is disabled due to limitations with the Lua debug package. If your configuration contains a lot of compute-heavy Lua code, you may experience better performance by setting the jit option to true.

Enabling the JIT may cause the instruction limit to behave inconsistently. If your configuration has infinite loops, waywall may freeze permanently.

Tearing

The tearing option allows you to enable screen tearing (it is disabled by default.) This option requires your compositor to support the tearing_control_v1 protocol, or else it will have no effect.

waywall

The waywall module contains various functions for interacting with waywall.

active_res

This function returns the current size of the Minecraft window (in pixels). If no resolution has been set with waywall.set_resolution(), the returned width and height values will equal zero.

Arguments

None

Return values

  • width: number
  • height: number

This function cannot be called during startup.

current_time

This function returns the number of milliseconds which have elapsed since an arbitrary epoch. The time is gathered using CLOCK_MONOTONIC.

Arguments

None

Return values

  • ms: number

exec

This function asynchronously executes the given command as a subprocess. No sophisticated argument processing is performed; quoting arguments or performing shell substitutions will require you to run a shell script. A maximum of 63 space-delimited arguments can be provided in the command string.

If the spawned subprocess does not exit before waywall, it will be killed with SIGKILL when waywall closes.

Arguments

  • command: string

Return values

None

This function cannot be called during startup.

floating_shown

This function returns whether or not floating windows (i.e. Ninjabrain Bot) are currently visible.

Arguments

None

Return values

  • shown: boolean

This function cannot be called during startup.

image

This function loads a PNG image from the file system and displays it over top of the waywall window. You may want to use it for e.g. your boat eye overlay.

The options table can have the following options, although only dst is required:

{
    -- absolute location/size of image in waywall window
    dst = {
        x = 100,
        y = 100,
        w = 200,
        h = 200,
    },

    -- optional
    shader = "shader_name"
}

For more information on custom shaders, see Shaders.

Image object

This function will return an object which can be used to remove the image from the waywall window at a later point. The only method made available by the returned image object is close.

If this object is not stored in a permanent location, the image may randomly disappear when it gets garbage collected.

Arguments

  • path: string
  • options: table

Return values

  • image: table

This function cannot be called during startup.

listen

This function registers the given event listener to be called whenever the given event occurs. The returned cancellation function can be called to unregister the event listener so that it is not triggered anymore.

The list of valid event names is:

  • resolution
    • For resolution changes with waywall.set_resolution()
  • state
    • For updates to the instance's state-output file

Arguments

  • event: string
  • listener: function

Return values

  • cancel: function

mirror

This function creates a "mirror" which displays a specific area of the Minecraft window on top of the waywall window. You may want to use it for e.g. boat eye measurement.

The options table can have the following options, although only src and dst are required:

{
    -- area to copy from minecraft window
    src = {
        x = 160,
        y = 900,
        w = 100,
        h = 100,
    },

    -- absolute location/size of mirror in waywall window
    dst = {
        x = 0,
        y = 300,
        w = 100,
        h = 100,
    },

    -- optional
    color_key = {
        input = "#dddddd",
        output = "#ee1111",
    },

    -- optional
    shader = "shader_name",
}

The color_key option allows you to perform color keying on the mirrored area, which will only preserve pixels of the given input color and change them to the output color.

For more information on custom shaders, see Shaders.

Mirror object

This function will return an object which can be used to remove the mirror from the waywall window at a later point. The only method made available by the returned mirror object is close.

If this object is not stored in a permanent location, the mirror may randomly disappear when it gets garbage collected.

Arguments

  • options: table

Return values

  • mirror: table

This function cannot be called during startup.

press_key

This function sends a fake key press (keydown event followed by a keyup event) to Minecraft using the given keycode. See Lookup Tables for a list of valid keycodes.

Arguments

  • key: string

Return values

None

This function cannot be called during startup.

profile

This function returns the name of the profile if one was provided at startup with --profile. Otherwise, it returns nil.

Arguments

None

Return values

  • profile: string or nil

set_keymap

This function attempts to change the current XKB layout in use by Minecraft. The following keys will be read from the options table if they are set:

  • layout
  • model
  • rules
  • variant
  • options

These keys behave the same as those in the input configuration table.

Arguments

  • options: table

Return values

None

This function cannot be called during startup.

set_resolution

This function sets the resolution of the Minecraft window. If both width and height are 0, the Minecraft window will instead stretch to fit the bounds of the waywall window (and will continue to do so as the window is resized).

Make sure your resolutions follow the leaderboard rules! You may only use a single resolution which extends past the bounds of your monitor, and it is not allowed to use a resolution greater than 16384 pixels in width or height.

Arguments

  • width: number
  • height: number

Return values

None

This function cannot be called during startup.

set_sensitivity

This function changes the mouse sensitivity multiplier which is used for camera movement. If the provided sensitivity is 0, the sensitivity will instead be reset to whatever value you have specified in the input configuration table.

Arguments

  • sensitivity: number

Return values

None

This function cannot be called during startup.

show_floating

This function sets whether or not floating windows (i.e. Ninjabrain Bot) should be visible.

Arguments

  • show: boolean

Return values

None

This function cannot be called during startup.

sleep

This function causes the current Lua execution context to pause for the given number of milliseconds. Other Lua code, such as other keybind or event handlers, can be executed while the current execution context is paused.

Calling this function forbids keybind handlers from marking an input as non-consumed. See Input consumption for more details.

Currently, it is only allowed to call this function from within a keybind handler. Support for calling this function from an event listener may be added at a later date.

Arguments

  • ms: number

Return values

None

This function cannot be called during startup.

state

This function returns a table describing the current state of the Minecraft instance according to the State Output mod. If the instance does not have State Output installed and enabled, this function will throw an error when called.

The returned table may have one of several structures depending on the state of the instance. The screen field will always be present, allowing you to determine which form it has:

title, waiting, and wall

{
    screen = "title" or "waiting" or "wall",
}

generating and previewing

{
    screen = "generating" or "previewing",
    percent = 0,
}

inworld

{
    screen = "inworld",
    inworld = "unpaused" or "paused" or "menu",
}

Arguments

None

Return values

  • state: table

This function cannot be called during startup.

text

This function creates and displays text over top of the waywall window.

Text object

This function will return an object which can be used to remove the text from the waywall window at a later point. The only method made available by the returned text object is close.

If this object is not stored in a permanent location, the text may randomly disappear when it gets garbage collected.

Arguments

  • text: string
  • x: number
  • y: number
  • color: string (optional)
  • size: number (optional)
  • shader: string (optional)

Return values

  • text: table

This function cannot be called during startup.

waywall.helpers

The waywall.helpers module contains various "helper" functions which provide common functionality.

ingame_only

This function wraps the given function so that it only runs if the user is in a world and not in any menu. If these conditions are not met, the given function will not be called and false will be returned.

If State Output is not enabled, calling the returned function will throw an error.

Arguments

  • func: function

Return values

  • ingame_func: function

The returned function cannot be called during startup.

toggle_floating

This function toggles whether or not floating windows are visible.

Arguments

None

Return values

None

This function cannot be called during startup.

toggle_res

This function returns another function which, when called, toggles between the provided resolution and no resolution (stretching Minecraft back to the bounds of the waywall window).

If a value is provided for the sens parameter, toggling to the given resolution will set the mouse sensitivity to that value. Toggling away from the resolution will set the sensitivity back to the default value specified in the input configuration table.

Arguments

  • width: number
  • height: number
  • sens: number (optional)

Return values

  • toggle: function

The returned function cannot be called during startup.

Lookup Tables

waywall has pre-defined sets of names which correspond to keys, mouse buttons, and modifiers. These are used when loading your configuration. All names are case insensitive.

Keycodes

The list of valid keycodes can be found in waywall's source code here.

Virtually all normal keyboard keys are present, although some may not be named as you expect. waywall follows the names defined by the input-event-codes.h header from Linux.

Keysyms

The list of valid keysyms comes from libxkbcommon. The xkbcommon-keysyms.h header contains all of the valid keysym names. Each keysym's name is prefixed with XKB_KEY_.

Modifiers

The following table lists all valid names for modifiers:

ModifierNames
Shiftshift
Controlctrlcontrol
Altaltmod1
Supermod4superwin
Caps Lockcapslockcapslock
Mod2mod2
Mod3mod3
Mod5mod5

Mouse buttons

The following table lists all valid names for mouse buttons:

ButtonNames
Left Buttonlmbm1mouse1leftmouse
Middle Buttonmmbm3mouse3middlemouse
Right Buttonrmbm2mouse2rightmouse
Side button (M4)mb4m4mouse4
Side button (M5)mb5m5mouse5

Lua changes

waywall makes use of LuaJIT, an alternative implementation of Lua 5.1 which provides better performance and additional functionality. A list of LuaJIT's additions is available here.

Note: The ffi and jit packages from LuaJIT are not available in waywall.

Instruction count limit

waywall will allow a maximum of 50 million Lua instructions to be executed when it calls into user code (e.g. actions). This limit is in place to prevent buggy configurations from hard-locking waywall.

If this limit is exceeded, an error will be thrown, causing Lua execution to stop. This error cannot be caught with pcall or xpcall.

Enabling the JIT may cause the instruction limit to behave inconsistently. If your configuration has infinite loops, waywall may freeze permanently.

Standard library changes

waywall makes a few changes and additions to the Lua standard library:

  • package.path is automatically updated to include the waywall configuration directory, so you can require() other files contained within it.
  • pcall and xpcall have been modified to prevent user code from disabling the instruction count limit by accident.
  • print has been modified so that its output appears in a similar format to other waywall log messages.
  • os.setenv is a new function added by waywall which behaves much like C's setenv() and allows for changing and deleting environment variables.
    • Calling os.setenv with two strings (a name and value) will behave like C's setenv().
    • Calling os.setenv with a string and nil will unset the given environment variable.

There are also a few breaking changes, which are mostly intended to prevent user code from causing problems within waywall's address space:

  • load, loadfile, and loadstring are not available

You can refer to the startup code to see all of the changes waywall makes in more detail.