Multiplexer (mux) events are emitted by WezTerm’s multiplexer layer, which manages terminal sessions, panes, tabs, and windows. These events allow you to customize multiplexer behavior and startup.
mux-startup
Since: 20220624-141144-bd1b7c5d
Emitted once when the mux server starts up.
Timing
- Fires before any default program is started
- Fires BEFORE
gui-startup event
- If this event creates panes, they take precedence over default program configuration
- Fires in both local and remote (daemon) mode
Event Signature
wezterm.on('mux-startup', function()
-- No parameters
end)
Return Value
No return value expected. This is a fire-and-forget event.
Examples
Basic Window Split
local wezterm = require 'wezterm'
local mux = wezterm.mux
-- Called when the mux server starts
wezterm.on('mux-startup', function()
local tab, pane, window = mux.spawn_window {}
pane:split { direction = 'Top' }
end)
return {
unix_domains = {
{ name = 'unix' },
},
}
Multi-Pane Development Layout
wezterm.on('mux-startup', function()
-- Create main window with editor
local tab, pane, window = mux.spawn_window {
cwd = wezterm.home_dir .. '/projects',
}
-- Create horizontal split for terminal
local bottom_pane = pane:split {
direction = 'Bottom',
size = 0.3,
}
-- Create vertical split in bottom for logs
bottom_pane:split {
direction = 'Right',
size = 0.5,
}
end)
Workspace Initialization
wezterm.on('mux-startup', function()
-- Create multiple workspaces
local workspaces = {
{ name = 'main', cwd = wezterm.home_dir },
{ name = 'dev', cwd = wezterm.home_dir .. '/dev' },
{ name = 'ops', cwd = '/var/log' },
}
for _, ws in ipairs(workspaces) do
mux.spawn_window {
workspace = ws.name,
cwd = ws.cwd,
}
end
mux.set_active_workspace 'main'
end)
Use Cases
- Setting up consistent terminal layouts
- Creating default workspaces
- Initializing multiplexer-only sessions (daemon mode)
- Pre-configuring pane arrangements before GUI attachment
mux-is-process-stateful
Since: 20220101-133340-7edc5b5a
Emitted when the multiplexer needs to determine if a pane can be closed without prompting.
Characteristics
- Synchronous event - Must return quickly to avoid blocking
- Called before closing a pane to check if confirmation is needed
- Allows custom logic beyond the default process name matching
Event Signature
wezterm.on('mux-is-process-stateful', function(proc)
-- proc is a LocalProcessInfo object
-- Return true, false, or nil
end)
Process information object containing details about the process tree in the pane.Fields:
pid - Process ID
name - Process name
status - Process status (e.g., “Sleep”, “Running”)
argv - Array of command-line arguments
executable - Full path to executable
cwd - Current working directory
children - Table of child processes (keyed by PID)
Return Values
Process is stateful - prompt user before closing
Process is not stateful - close without prompting
Use default behavior (check skip_close_confirmation_for_processes_named config)
Examples
Log Process Tree
local wezterm = require 'wezterm'
function log_proc(proc, indent)
indent = indent or ''
wezterm.log_info(
indent
.. 'pid='
.. proc.pid
.. ', name='
.. proc.name
.. ', status='
.. proc.status
)
wezterm.log_info(indent .. 'argv=' .. table.concat(proc.argv, ' '))
wezterm.log_info(
indent .. 'executable=' .. proc.executable .. ', cwd=' .. proc.cwd
)
for pid, child in pairs(proc.children) do
log_proc(child, indent .. ' ')
end
end
wezterm.on('mux-is-process-stateful', function(proc)
log_proc(proc)
return nil -- Use default behavior
end)
return {}
Example Output:
INFO config::lua > lua: pid=1913470, name=zsh, status=Sleep
INFO config::lua > lua: argv=-zsh
INFO config::lua > lua: executable=/usr/bin/zsh, cwd=/home/user
INFO config::lua > lua: pid=1913567, name=bash, status=Sleep
INFO config::lua > lua: argv=bash
INFO config::lua > lua: executable=/usr/bin/bash, cwd=/home/user
INFO config::lua > lua: pid=1913624, name=vim, status=Sleep
INFO config::lua > lua: argv=vim foo
INFO config::lua > lua: executable=/usr/bin/vim, cwd=/home/user
Custom Stateful Process Detection
-- Check if any process in the tree is an editor
local function is_editor_running(proc)
local editors = { 'vim', 'nvim', 'emacs', 'nano', 'code' }
for _, editor in ipairs(editors) do
if proc.name == editor then
return true
end
end
-- Check children recursively
for _, child in pairs(proc.children) do
if is_editor_running(child) then
return true
end
end
return false
end
wezterm.on('mux-is-process-stateful', function(proc)
if is_editor_running(proc) then
return true -- Always prompt when editor is running
end
return nil -- Use default for other processes
end)
Directory-Based Detection
wezterm.on('mux-is-process-stateful', function(proc)
-- Always prompt if working in important directories
local protected_dirs = {
'/etc',
wezterm.home_dir .. '/important-project',
}
for _, dir in ipairs(protected_dirs) do
if proc.cwd:find(dir, 1, true) == 1 then
wezterm.log_info('Process in protected directory: ' .. proc.cwd)
return true
end
end
return nil
end)
Long-Running Process Detection
-- Track process start times (simplified example)
local process_times = {}
wezterm.on('mux-is-process-stateful', function(proc)
local now = os.time()
if not process_times[proc.pid] then
process_times[proc.pid] = now
end
local runtime = now - process_times[proc.pid]
-- Prompt if process has been running for more than 1 hour
if runtime > 3600 then
wezterm.log_info(
string.format('Long-running process detected: %s (%ds)',
proc.name, runtime)
)
return true
end
return nil
end)
Use Cases
- Custom logic for determining important processes
- Protecting specific workflows or directories
- Integration with project-specific tools
- Preventing accidental closure of development environments
Event Ordering
When WezTerm starts, multiplexer events fire first:
1. mux-startup ← Multiplexer initializes
2. gui-startup ← GUI layer starts
3. gui-attached ← GUI attaches to domain
For wezterm connect DOMAIN:
1. mux-startup ← Mux starts (if new daemon)
2. gui-attached ← GUI attaches to domain
(gui-startup does NOT fire)
Common Patterns
Daemon Mode Setup
local wezterm = require 'wezterm'
local mux = wezterm.mux
wezterm.on('mux-startup', function()
-- Setup for daemon/server mode
local home = wezterm.home_dir
-- Create a persistent development workspace
local dev_tab, dev_pane, dev_window = mux.spawn_window {
workspace = 'dev',
cwd = home .. '/dev',
}
-- Bottom pane for build output
local build_pane = dev_pane:split {
direction = 'Bottom',
size = 0.25,
}
-- Create monitoring workspace
local mon_tab, mon_pane, mon_window = mux.spawn_window {
workspace = 'monitoring',
args = { 'htop' },
}
-- Default to dev workspace
mux.set_active_workspace 'dev'
end)
return {
unix_domains = {
{ name = 'unix' },
},
}
Combining mux-startup with gui-startup
-- mux-startup: Create persistent structure
wezterm.on('mux-startup', function()
-- This runs once when mux starts
mux.spawn_window { workspace = 'persistent' }
end)
-- gui-startup: Configure GUI-specific settings
wezterm.on('gui-startup', function(cmd)
-- This runs each time GUI starts
local tab, pane, window = mux.spawn_window(cmd or {})
window:gui_window():maximize()
end)
See Also