Lua Console
Run Lua against the session API or execute a Lua file from the command line.
Interactive and file modes require a running Gnoblin session. Lua runs locally through gnoblinctl.
Lua console
Run gnoblinctl lua inside a Gnoblin session to inspect and control the live compositor with the Lua API. Enter one Lua statement or expression per line. Expressions print their result; tables print as JSON. Type :help for the console commands and :quit to exit.
Read build and source identity with gnoblin.version():
local version = gnoblin.version()
print(version.gnoblin, version.gnome, version.git_sha)=gnoblin.windows.list { focused = true }
=gnoblin.workspaces.list()
:quitWindow reads return read-only records with the same properties and typed methods as the supervised runtime:
local window = gnoblin.windows.focused()
if window then
print(window.id .. ": " .. window.title)
print(window:minimize().id)
endWindow actions wait for compositor completion and return the completed value. They do not return an asynchronous Operation handle.
focus, begin_move, and begin_resize require a trusted FocusContext from an event callback. The ordinary REPL cannot create one or call these methods.
The same context authorizes keyboard snapping. Inside the event callback, call gnoblin.windows.snap_context(event.focus_context), then context:commit({monitor_id, frame}) to apply its frame.
In gnoblinctl lua, both calls wait for their results on the event connection.
gnoblin.focus.history(filter?) returns read-only Window records in most-recently-focused order. The optional filter accepts string workspace and monitor IDs plus an integer limit from 1 to 256 (default 50):
for _, window in ipairs(gnoblin.focus.history { limit = 10 }) do
print(window.id, window.title)
endRead the committed focus behavior through the read-only FocusPolicy property:
local policy = gnoblin.focus.policy
print(policy.focus_mode, policy.focus_new_windows, policy.revision)gnoblin.settings returns the committed settings snapshot as a deeply read-only value. Read nested options directly from the snapshot:
local settings = gnoblin.settings
print(settings.window_management.focus_mode, settings.revision)Reload the Lua configuration from the console when it changes. The operation returns a deeply read-only result; inspect it to see what was applied:
local result = gnoblin.runtime.reload_config()
print(result.action, result.runtime_generation)gnoblin.privacy.state() returns a deeply read-only PrivacyState record. Check available before reading an activity field; unavailable sources omit their activity value:
local privacy = gnoblin.privacy.state()
if privacy.available.screen_sharing then
print(privacy.screen_sharing and "Sharing" or "Not sharing")
endThe console can ask Mutter to stop tracked screen-sharing or recording sessions. The result reports how many stop requests were issued; it does not confirm that the sessions have closed:
local sharing = gnoblin.privacy.stop_sharing()
local recording = gnoblin.privacy.stop_recording()
print(sharing.requested, recording.requested)gnoblin.capabilities.list() returns read-only Capability records with the capability ID, description, availability, revision, and an optional reason:
for _, capability in ipairs(gnoblin.capabilities.list()) do
print(capability.id, capability.available, capability.reason)
endRead launch feedback with gnoblin.launches.list(). Use gnoblin.launches.snapshot() when you also need the collection revision. Both calls return deeply read-only launch records:
local snapshot = gnoblin.launches.snapshot()
for _, launch in ipairs(snapshot.launches) do
print(launch.application, launch.state, snapshot.revision)
endThe console can also begin and finish launch feedback. Call finish() with the same token when the launch is cancelled or the application has started:
local token = "launcher-42"
local launch = gnoblin.launches.begin {
token = token,
application = "org.example.Editor",
timeout_ms = 3000,
}
print(launch.state)
gnoblin.launches.finish(token)gnoblin.permissions.policy() returns the committed permission policy as a deeply read-only PermissionPolicy record:
local policy = gnoblin.permissions.policy()
print(policy.default, policy.revision)
for _, rule in ipairs(policy.rules) do
print(rule.name, rule.level)
endUse gnoblin.permissions.list() to also read the supported capabilities, permission levels, and configuration path. Its nested values are deeply read-only.
Check how the committed policy evaluates a capability for an application identity. The console accepts either two strings or a table:
local decision = gnoblin.permissions.check {
capability = "remote-desktop",
identity = "app-id:org.example.Remote",
}
print(decision.level, decision.rule, decision.revision)gnoblin.session.status() returns a read-only SessionStatus. When lock state is unavailable, lock_state is omitted; that does not mean the session is unlocked. revision identifies the current lock-state snapshot and matches the revision on its corresponding lock-state event:
local status = gnoblin.session.status()
print(status.state, status.revision)
if status.lock_available then
print(status.lock_state)
endgnoblin.session.activity() returns the latest idle-monitor sample as a read-only SessionActivity record:
local activity = gnoblin.session.activity()
if activity.available then
print(activity.idle, activity.idle_for_ms, activity.threshold_ms)
endgnoblin.session.lock() asks a subscribed shell client to show its lock UI. Its read-only result reports delivery, not that the compositor locked the session. Check gnoblin.session.status() for the confirmed lock state. gnoblin.session.logout() ends the session and returns {accepted = true} before the compositor shuts down:
local request = gnoblin.session.lock()
print(request.dispatched, request.subscribers)Workspace snapshots are available through gnoblin.workspaces.list(), gnoblin.workspaces.active(), and gnoblin.workspaces.by_id(id). They return read-only Workspace records. The console also exposes the workspace operations available to configuration scripts:
local workspace = gnoblin.workspaces.by_id("web")
if workspace then
workspace:activate()
workspace:move_here("42", { follow = true })
endWorkspace actions wait for compositor completion and return the completed value. The console supports these namespace methods:
create {name, id?, activate?}rename {id|number, name},activate {id|number}, andremove {id|number}next()andprevious()move_active {workspace, follow?}andmove_window {window, workspace, follow?}
A workspace selector is a table with exactly one of id or positive number. Creation accepts a nonempty name up to 80 characters. An optional ID must start with an ASCII letter or digit and contain up to 64 letters, digits, periods, underscores, or hyphens. activate and follow must be booleans when supplied. A window is "active" or a stable window ID.
move_active and move_window return a read-only WorkspaceMove with the resulting workspace, window ID, and follow value. The compositor rejects removal of configured, active, or nonempty workspaces.
Input devices and sources are read-only InputDevice and InputSource records. Each record includes the revision of its snapshot. The current source is nil when Gnoblin cannot identify one. Select a listed source by passing its type and id:
for _, source in ipairs(gnoblin.input.sources()) do
if source.current then
print(source.name)
end
end
local selected = gnoblin.input.select_source { type = "xkb", id = "us" }
print(selected.name)Source selection waits for Mutter or IBus to confirm the change and returns the completed InputSource record.
Monitor snapshots are available through gnoblin.monitors.list() and gnoblin.monitors.primary(). They return read-only Monitor records, or nil when the session has no primary monitor:
for _, monitor in ipairs(gnoblin.monitors.list()) do
print(monitor.id, monitor.width, monitor.height, monitor.scale)
endUse gnoblin.layers.list(filter?) to read layer surfaces. Each result is a read-only LayerSurface record. gnoblin.layer.list() is the compatibility spelling for the same snapshot.
The optional filter matches these fields exactly:
monitor_id: the monitor's stable connector ID.namespace: the layer surface namespace.layer:background,bottom,top, oroverlay.
for _, surface in ipairs(gnoblin.layers.list { layer = "top" }) do
print(surface.namespace, surface.geometry.width)
endRead the effective animation and shadow policy for a layer namespace with gnoblin.layers.animation_policy(namespace). It returns a deeply read-only LayerAnimationPolicy record:
local policy = gnoblin.layers.animation_policy("bingux-panel")
print(policy.enter.animation, policy.exit.animation, policy.window_shadow)Animation previews return a read-only AnimationPreview record. Its seek, step, play, and pause methods return the completed updated record; stop returns its completion result:
local preview = gnoblin.animations.preview {
name = "gnoblin-window-open",
event = "open",
target_type = "window",
target = "active",
}
preview = preview:seek(0.5)
print(preview.progress)
preview:stop()Portal grants are read-only PortalGrant records. Filter by kind and revoke a grant from the snapshot:
for _, grant in ipairs(gnoblin.portals.grants { kind = "remote-desktop" }) do
print(grant.requester, table.concat(grant.devices, ", "))
grant:revoke()
endRevocation uses the record's creation time, so a stale record cannot revoke a new grant that reuses its ID. The console waits for each revoke to complete. Both PortalGrant:revoke() and gnoblin.grant.revoke(args) return deeply read-only results. Scripts can also use the shared gnoblin.grant.revoke call directly; pass the timestamp from the snapshot to keep the same stale-record check:
local grants = gnoblin.portals.grants { kind = "remote-desktop" }
local grant = grants[1]
if grant then
gnoblin.grant.revoke {
kind = grant.kind,
id = grant.id,
created_at = grant.created_at,
}
endShortcut snapshots return read-only ShortcutState and ShortcutAction records. gnoblin.shortcuts.actions(group?) accepts an optional wm, mutter, or wayland group:
for _, shortcut in ipairs(gnoblin.shortcuts.list()) do
print(shortcut.name, shortcut.binding, shortcut.enabled)
end
for _, action in ipairs(gnoblin.shortcuts.actions("wm")) do
print(action.id, action.default_bindings[1])
endCapture a shortcut from the terminal console. The call waits for the key press and returns the normalized accelerator. The timeout defaults to 30 seconds and can be set from 1 to 60 seconds; Escape cancels capture.
local captured = gnoblin.shortcuts.capture({timeout = 10})
print(captured.accelerator)To run a Lua file with the same API, pass its path:
gnoblinctl lua ./inspect.luaThe Lua code runs in the local gnoblinctl process. Session calls go through the compositor's typed API, which validates methods and arguments. gnoblinctl never sends Lua source to the compositor.
The console can subscribe to runtime events. gnoblinctl lua stays open while any subscription is active. A once subscription ends after its event; call subscription:unsubscribe() to stop a repeating listener. Press Ctrl+C to stop a script that continues listening.
For worker health, read gnoblin.runtime.status() after subscribing to gnoblin.runtime.status-changed. The event reports recovery and accepted configuration-generation changes while the compositor socket is connected; see the runtime API guide for its fields and reconnect behavior.
To handle a location request, subscribe to gnoblin.location.authorization-requested. The CLI can answer only a request received by an active subscription, and sends the reply over that same event connection. Requests expire after 25 seconds. This example denies the request; replace the decision with a consent prompt before approving access:
local subscription
subscription = gnoblin.events.on("gnoblin.location.authorization-requested", function(event)
local result = gnoblin.location.authorize_app {
request_id = event.request_id,
allow = false,
accuracy = 0,
}
print(result.submitted)
subscription:unsubscribe()
end)Approval must choose an accuracy level no greater than the one requested by the app. The call returns a read-only result; submitted confirms Gnoblin accepted and queued the answer. System settings can still limit approved accuracy.
gnoblinctl lua can register, remove, and manage shortcut sessions. Bindings created by the console belong to its compositor connection, so the process stays open while one of its bindings remains registered. Unbind the shortcut or exit the process to release it. See the shortcut API reference for the accepted binding options.
For example, save this as shortcut.lua and run gnoblinctl lua ./shortcut.lua:
local binding = gnoblin.shortcuts.bind {
id = "temporary-cli-shortcut",
accelerator = "<Alt>F12",
hold = "alt",
mode = "modal",
}
local subscription
subscription = gnoblin.events.on("gnoblin.shortcut.session.activated", function(event)
if event.id ~= binding.id then
return
end
gnoblin.shortcuts.end_session {
id = event.id,
session_id = event.session_id,
}
gnoblin.shortcuts.unbind { id = binding.id }
subscription:unsubscribe()
end)The example ends the active modal session, removes its temporary binding, and lets the CLI exit. If you stop the process first, its connection closes and Gnoblin removes the binding.
A Lua event listener can use a FocusContext delivered with a trusted event to focus a window or start an interactive move or resize. The context is an opaque, one-use value and the console sends the request on the event's connection.
For example, save this as focus.lua, replacing the ID with one from gnoblinctl window list:
local window = gnoblin.windows.by_id("replace-with-window-id")
assert(window, "window no longer exists")
gnoblin.events.on("gnoblin.shortcut.activated", function(event)
if event.focus_context then
window:focus(event.focus_context)
end
end)Run it with gnoblinctl lua ./focus.lua while a configured shortcut emits gnoblin.shortcut.activated. Press Ctrl+C to stop the listener.
WM window-menu requests carry an opaque MenuContext in event.menu_context. Call :begin_move() or :begin_resize(edge) on that value to use the one-time authority for the window that raised the menu. The console keeps its token private and sends the operation over the event's connection. Application-menu requests have no MenuContext.