The Lua API
Everything the compositor puts in the sol table, transcribed from sol.lua, the definitions file lua-language-server reads for completion (how to set it up). A test fails when the compositor and that file disagree about any of its 98 names.
Most calls queue a command and return nothing. The commands a handler queues are applied together when it returns, in the order it queued them, and every animated one uses the timing sol.animate last set in that handler. Desktop modes is the guide to using them.
Events
What sol.on can listen for.
| Event | Handler arguments and meaning |
|---|---|
open | A window’s life began: (id). |
focus | The keyboard moved to a window: (id). |
activate | A window asked to be brought forward, after the compositor answered: (id, why). why is "launch" when an application already running answered a launch with a window it had, and "request" for anything else. |
closing | A close was asked for and the window is fading: (id). |
refused | Its application declined, and the window is back: (id). |
close | The window is gone: (id). |
fullscreen | A window went fullscreen, or left it: (id, entering). Told once the change is made: its client has been sent its new size and it lives at its new rectangle. Once every listener has run, the compositor moves its picture there from where it is drawn, as a layout’s glide does, with the timing a listener set with sol.animate, and at once if none set one. Until the client answers at its new size the window is held there and its last picture stretched into it, for a quarter of a second past the landing at most; the transform is released when the glide lands. With no timing the change puts no transform on the window and holds nothing. A window a mode is presenting is left where the mode draws it. A change a listener makes is made at once and not told. |
maximize | A window was maximised, or restored: (id, entering). Told and answered as "fullscreen" is, and on its own, so the two can move differently. |
drop | A dragged window was let go: (id, x, y). |
resize | An edge is being dragged: (id, edge_x, edge_y, horizontal_side, vertical_side). |
scroll | The wheel turned with Super held: (dx, dy). |
click | A press while a script holds input: (x, y). |
surface | A hosted scene sent an action: (surface, action, data), data a table, a value or nil. Every action is heard, in the order sent, in a dispatch after the one that sent it; a scene that sets an action string property is heard the same way, with data nil. |
direction | sol.focus_direction or sol.move_direction was called: (verb, dir), verb being "focus" or "move". The layout in charge answers. |
layout | Arrange the windows you already hold again: (). |
monitors | The monitors changed, or were announced at startup or after a reload: (). |
restore | These scripts replaced a running session’s, after a reload and never at startup: (). |
text_input | The focused text field changed: (field, why), field being what sol.text_input() answers at that moment and why what changed – "field", one was enabled, or focused again by its window getting the keyboard back; "caret", its client moved its caret; "framed", its window started or stopped being drawn in a frame, as one going fullscreen does. Told at most once a pass of the event loop, before the frame is drawn, as the most that changed: a caret moved twice since the last frame is told once, where it is now, and a field enabled and then given its caret, as kitty gives it with its first key, is told once as "field". A window moving with its field in it is not told. A field disabled, or its window losing the keyboard, is not told: sol.text_input() answers nil from then on, and field is never nil here. |
keyboard | The live layout, Caps Lock or Num Lock changed, by a key or by sol.keyboard{ ... }: (state, changed), state being what sol.keyboard() answers now and changed "layout", "caps" or "num". Never for ordinary typing, a new keymap or a configuration starting, nor while the session is locked: what is pressed at the lock screen is the lock screen’s, and a lock screen that wants a Caps Lock warning draws its own. A change a keyboard listener makes is not told back to it. |
sol
sol.keep(name, defaults)
State that survives super+shift+r.
Answers the table the last configuration kept under name, or defaults on the first load. Mutate it in place and the next reload gets what you left in it. It may hold numbers, strings, booleans, tables of those, and the trees and strips sol.layout makes, eight tables deep; anything else is named in the log and left out. Asked for twice under one name, it is the same table both times.
Parameters:
name:stringdefaults:table
Returns table.
sol.windows()
Every window a layout may arrange, topmost first.
A snapshot taken for this handler, not a live view. A window that closes while you hold its id is ordinary: place and present on an id that no longer exists do nothing.
Returns sol.Window[].
sol.apps()
Every installed, visible application (NoDisplay and Hidden entries are left out at the source), by name.
A snapshot taken for this handler, not a live view, and – unlike sol.windows() and sol.monitors() – empty at the top level of a configuration file: it reads before the first real one arrives, and before the index has even scanned once on a first start. A scene reads Solium.Apps instead, which is live from the moment it is drawn.
Returns sol.App[].
sol.surface(name, options)
Draw a QML scene: a wallpaper, a bar, a dock, a heads-up display.
Declaring a name again with the same scene changes the live scene: changed properties are written into it, and on a monitor it was already on a new on, layer or interactive keeps it, so what the scene holds survives (an open popup, a running animation). A monitor it newly covers gets a scene built from the current properties, and one it leaves drops its scene. Only another scene file builds it again. A key left out of properties keeps the value the scene last had. A dotted key (["panel.open"] = true) reaches a grouped property. false or nil takes the surface away. Each monitor it is on gets a scene of its own, built when the surface is declared or the monitor arrives and dropped when the monitor goes; Solium.monitor inside it is that monitor. A scene file that is not there is logged, and built once it is there, at the next reload, change of monitors or changed declaration; a scene that will not load is logged once per monitor and not tried again until another scene is declared or the configuration is reloaded.
Parameters:
name:stringoptions:sol.SurfaceOptions|false|nil
sol.keyboard(options)
Read the keyboard, or change it.
With no argument, answers the layouts, which one is live, its short name, whether Caps Lock and Num Lock are on, and how keys repeat. With a table, changes what it names and leaves the rest alone, so a binding that switches layout does not reset the repeat rate. sol.on("keyboard", ...) hears the layout and the locks change.
Also called as:
sol.keyboard()returnssol.KeyboardState
Parameters:
options:sol.KeyboardOptions(optional)
Returns sol.KeyboardState|nil.
sol.text_input()
The focused text field and where its caret is, or nil when the window with the keyboard has no text field enabled.
Kept by the text-input-v3 protocol: an application with a text field focused says so, and says where its caret is. Applications that do not speak it, and X11 ones, never have one. sol.on("text_input", ...) hears a field being enabled or focused, its caret moving, and its window being framed or left bare.
Returns sol.TextField|nil.
sol.monitors(rows)
Read the monitors, or arrange them.
With no argument, answers every monitor. With a list, drives and places the monitors it names; a row with no name is logged and skipped.
Also called as:
sol.monitors()returnssol.Monitor[]
Parameters:
rows:sol.MonitorPlacement[](optional)
Returns sol.Monitor[]|nil.
sol.workspaces(declared)
Say what the workspaces are. The compositor does not know what a workspace is: workspaces.lua is the whole feature, and this is how a hosted shell’s Workspaces model learns it. Declare again whenever it changes; the same declaration twice costs nothing. Monitors and windows the compositor does not have are left out, with any group left with no monitor, and so is a workspace whose <group>/<id> an earlier one in a group that stays already has; each is logged once.
Parameters:
declared:sol.WorkspaceDeclaration
sol.monitor_power(which, mode)
Turn a monitor’s display off or on, or every monitor’s with "all".
The monitor keeps its place, its work area and its windows. Any key, click, scroll, touch or pointer motion turns every screen back on. A mode that is neither "on" nor "off" is an error; a name no monitor has is a line in the log.
Parameters:
which:string— A monitor’s name, or"all".mode:"on"|"off"
sol.idle(options)
Set the idle blank: config.idle, which the shipped init.lua hands over.
Parameters:
options:sol.IdleOptions(optional)
sol.monitor(id)
The work area of the monitor a window is on, or of the active monitor when no window is named or the id is unknown.
Parameters:
id:integer(optional)
Returns sol.Rect.
sol.cursor()
Where the pointer is.
Returns sol.Point.
sol.window_at(x, y, skip)
The window drawn at a point, asked the way the compositor asks it: against where windows are drawn, on the monitors that draw them.
skip leaves one window out, which is what makes it useful while dragging: the dragged window is always the one under the pointer.
Parameters:
x:numbery:numberskip:integer(optional)
Returns integer|nil (id).
sol.present(id, options)
Draw a window somewhere other than where it lives, animated.
A transform: the window keeps its place and its client is told nothing, so sol.present_clear puts it back exactly. Input follows the rect it is drawn at.
A present that lands exactly on the window’s own frame (no rect, no rotation, full opacity) lets the window go, as sol.present_clear does, rather than holding it there.
Parameters:
id:integeroptions:sol.PresentOptions(optional)
sol.present_from(id, options)
Draw a window at a rect and animate it to where it lives: every “appears from somewhere” animation. The rect is required.
Parameters:
id:integeroptions:{ x: number, y: number, w: number, h: number, opacity?: number }
sol.debug_mode()
Whether the compositor was started with --debug-mode or with SOLIUM_DEBUG_MODE set. The Developer Tweaks panel asks this.
Returns boolean.
sol.reload()
Read the configuration again, without ending the session.
A configuration that fails to load leaves the running one in place. See sol.keep and the restore event for what a reload carries.
sol.pane(name)
Choose how every window is framed, at once.
A name is a folder under ~/.config/solium/qml/panes or the shipped qml/panes, or a single file under ~/.config/solium/qml/decorations; a path is anyone’s; "none" draws no frame. SOLIUM_PANE, when set, wins over the name given here.
Parameters:
name:string(optional)
sol.pane_values(values)
Hand values of the configuration’s own to every layer of every pane.
Every layer’s root reads them as one object, values, which PaneStyle declares for an inline layer and a delegated layer declares itself (property var values: ({})): so a setting the configuration reads can reach the QML that draws by it, and the compositor never knows what the setting is. Each call merges its keys into what is there, so two scripts each handing their own keep both; a key, once given, keeps its last value until the configuration is reloaded, which starts with none. Values are what sol.surface’s properties take. A window opened later is told them too.
Parameters:
values:table
sol.decorations()
Every frame style sol.pane could be given, found in the folders the compositor looks in: sorted, bundles first, each name once.
Returns sol.DecorationEntry[].
sol.loading(options)
Set what a window does between being asked for and its application arriving.
Parameters:
options:sol.LoadingOptions
sol.client_sizes(options)
Set whose own size limits a floating window’s edge drag is held to.
floating = "ignore" lets the drag go wherever the pointer does; anything else believes every application. ignore lists applications, by app_id, whose limits are never believed.
Parameters:
options:{ floating?: "respect"|"ignore", ignore?: string[] }(optional)
sol.resize(options)
Set what fills a window while a resize drag is ahead of its client.
"stretch" (the default) scales the last picture; "hold" leaves it at its own size; "scene" draws the window’s QML scene, which today reaches only a window resized before its application painted. Another name is logged and the default kept.
Parameters:
options:{ fill?: "stretch"|"hold"|"scene" }(optional)
sol.fullscreen(options)
Set what a fullscreen window covers.
"top" (the default) covers the top layer, so bars go under it; "none" leaves bars over it. Another name is logged and the default kept.
Parameters:
options:{ covers?: "top"|"none" }(optional)
sol.cursor_theme(options)
Set the pointer’s XCursor theme, size and scene. Applied at once, so a reload is how a theme or a scene is tried, and a scene whose files changed is built again. Not sol.cursor, which says where the pointer is.
Parameters:
options:sol.CursorOptions(optional)
sol.qml(options)
Choose how QML renders, and how long the GPU trial may take.
Read once, when the compositor starts, before any scene does; a reload does not change it. --qml, SOLIUM_QML and SOLIUM_QML_GPU override it.
Parameters:
options:sol.QmlOptions(optional)
sol.session(options)
Say how this session is announced, and how long a signal to end waits.
Read once, when the compositor starts; a reload does not change it. Who is told: the session bus, when Solium is started as the session with solium --tty --session; nobody, for a --tty start by hand or a nested run, unless SOLIUM_SESSION_BUS names a bus to tell instead. The shipped init.lua calls it with config.session.
Parameters:
options:sol.SessionOptions(optional)
sol.present_clear(id)
Stop presenting a window: animate it back to where it lives.
Parameters:
id:integer
sol.group(name, selection)
Name a selection of windows, surfaces and monitors, so one transform can carry them together.
Declaring the name again replaces the membership and keeps the transform, so a mode that rebuilds its groups on every pass does not restart its own animation. false or nil takes the name away.
Parameters:
name:stringselection:sol.Selection|false|nil
sol.present_group(name, shift, motion)
Move, fade or turn a selection as one.
Composes with each member’s own sol.present, so a window tilted inside a moving desk stays tilted within it. motion sets this call’s own timing; left out, it is whatever sol.animate set.
Parameters:
name:stringshift:sol.Shift(optional)motion:sol.Motion(optional)
sol.present_group_clear(name, motion)
Animate a selection back to where its members live.
Parameters:
name:stringmotion:sol.Motion(optional)
sol.animate(options)
Set the timing for every animated command queued after this one in the same handler. Left unset, it is 220 ms and "outCubic". In a "fullscreen" or "maximize" listener it is also how the window’s own move is timed; there, left unset, the move is instant.
Parameters:
options:sol.Motion
sol.focus(id)
Give a window the keyboard.
Parameters:
id:integer
sol.toggle_fullscreen(id)
Make a window fullscreen, or give it back its place if it already is. With no id, the focused window; with nothing focused, nothing happens.
Parameters:
id:integer(optional)
sol.toggle_maximize(id)
Maximise a window to its monitor’s work area, or give it back its place if it already is. With no id, the focused window; with nothing focused, nothing happens.
Parameters:
id:integer(optional)
sol.focus_direction(dir)
Move the keyboard to the window beside the focused one. Every direction listener is told ("focus", dir), and the layout in charge answers (lua/direction.lua). Anything but the four directions is an error. The listeners’ time counts against the handler that called this, so one stopped at the deadline stops that handler too.
Parameters:
dir:"left"|"right"|"up"|"down"
sol.move_direction(dir)
Move the focused window in a direction: direction listeners are told ("move", dir). Tiling trades it with its neighbour, scrolling moves it within or between columns, and a fullscreen or maximised window moved to another monitor stays so there. Anything but the four directions is an error. The listeners’ time counts against the handler that called this, so one stopped at the deadline stops that handler too.
Parameters:
dir:"left"|"right"|"up"|"down"
sol.place(id, options)
Put a window where it lives: the layout’s call.
The client is told the size and asked to redraw. The rect is a tile unless tile = false: a client that will not shrink to it is cut to it rather than drawn over its neighbours. A rect is required.
Parameters:
id:integeroptions:sol.PlaceOptions
sol.unplace(id)
Let a window go from its tile, when a layout stops arranging it. modes.use sends this for every window when the layout in charge changes.
Parameters:
id:integer
sol.close(id)
Ask a window to close. A request, not a kill: its application may refuse.
Parameters:
id:integer
sol.act(action, data, done)
Perform an action of the vocabulary: the same names a scene sends with Solium.send, windows.focus, windows.close, windows.fullscreen and windows.maximize, each with { id = <window id> }; the last two toggle. data is handed over as a surface’s properties are, so a table nested deeper than 64 or holding more than 65 536 values, or one that contains itself, is an error in the handler. Queued like every other request, and answered by done(ok, reason) once the compositor has acted, in a handler of its own; reason is "unknown-action", "unknown-window", "bad-data", for a windows.focus behind the lock "locked", or, for a windows.fullscreen or windows.maximize of an X11 window, which the compositor cannot send there, "unsupported". A window still loading is "unknown-window" to all but windows.close. A done that calls sol.act again is told again in the same dispatch, 16 rounds at most, and the rest at the next one. Answers the attempt’s id, one no earlier sol.act answered. A workspaces.* action is not the compositor’s: the file that keeps the workspaces answers it, with actions.override in lua/actions.lua. An override is a surface listener of its own, under the same deadline, so one stopped three times is taken out alone, and its action goes to sol.act again.
Also called as:
sol.act(action: "windows.focus"|"windows.close"|"windows.fullscreen"|"windows.maximize", data: { id: integer }, done?: fun(ok: boolean, reason?: string))returnsinteger
Parameters:
action:stringdata:any(optional)done:fun(ok: boolean, reason?: string)(optional)
Returns integer (attempt).
sol.spawn(program, ...)
Start a program, with its arguments as separate strings: sol.spawn("foot", "-e", "htop").
The program gets the environment Solium was started with, not what Qt and the libraries it loads have written into Solium’s own since. On top of that: WAYLAND_DISPLAY naming this compositor; DISPLAY naming its own Xwayland, or removed when there is none; XDG_CURRENT_DESKTOP, which is Lilium unless the session named one; XDG_SESSION_TYPE=wayland when it is unset; and XDG_ACTIVATION_TOKEN and DESKTOP_STARTUP_ID, naming the window opened for it. It holds no file descriptor beyond stdin, stdout and stderr. stdin and stdout are /dev/null, and stderr is Solium’s.
Parameters:
program:string...:string
sol.which(program)
Whether a program can be found on PATH. A name with a / in it is checked as a path. Nothing is run.
Parameters:
program:string
Returns boolean.
sol.quit()
End the session. Ctrl+Alt+Backspace does the same and cannot be rebound.
sol.grab_input(grabbed)
Take input away from clients, or give it back.
While a script holds it, keys it has not bound are swallowed, pointer presses go to click listeners, and focus stops following the pointer. Release it when you leave, or nothing reaches a window again.
Parameters:
grabbed:boolean
sol.status(text)
Say which mode is in charge. The compositor keeps the text and logs a change at debug level, and a hosted shell reads it as Solium.status; nothing the compositor draws itself shows it.
Parameters:
text:string
sol.bind(combo, handler, note)
Bind a key combination to a function.
The combination is normalised, so super+shift+q and Shift+Super+Q are one binding. A later call for the same combination replaces the earlier one. While the session is unlocked, a bound combination never reaches the application with the keyboard. A key a mode needs only while it is up is bound when the mode starts and taken away with sol.unbind when it stops, as overview.lua does with escape. note is a short phrase saying where the binding came from, which solium --check prints beside it.
Parameters:
combo:stringhandler:fun()note:string(optional)
sol.bound(combo)
Whether a combination is bound, in whatever spelling it is given.
Parameters:
combo:string
Returns boolean.
sol.unbind(combo, note)
Take a binding away. The combination then reaches the application with the keyboard again. With a note, solium --check lists the combination as removed by the configuration.
Parameters:
combo:stringnote:string(optional)
sol.unknown(key, meant)
Report a setting nothing reads. config.lua calls this for each key of a user.lua its defaults do not define; solium --check lists them and exits with 1. meant is a near-miss suggestion.
Parameters:
key:stringmeant:string(optional)
sol.on(event, handler)
Listen for an event. Listeners are added, never replaced, and one that fails is logged while the others still run.
A listener runs under a 100 ms deadline: one that takes longer is stopped with an error and logged with the file and line it was written at and the file and line it was stopped at (for an actions.override, the listener actions.lua writes for it, and the override’s own line), the other listeners still run, and one stopped three times is taken out until the configuration is reloaded. The stops are counted by function, so a function listening for two events counts the stops of both, and from its third on is taken out of each event it is stopped in. Bindings, and each done of sol.act, run under the same deadline, and a stopped binding is logged the same way. A done is struck by function too, and one stopped three times is not called again until the configuration is reloaded; a done written inline is a new function at each sol.act, so it is stopped each time rather than taken out. What a done asked for in the run that was stopped is dropped. The compositor starts each one’s clock itself, so nothing a handler calls puts it off. pcall, xpcall and load hand the stop on rather than catch it, and xpcall’s message handler is not called for it. A __gc finalizer, and the __close of a to-be-closed variable in the function a stop interrupts, run where no hook does, so the deadline cannot stop a loop in either.
Also called as:
sol.on(event: "open"|"focus"|"closing"|"refused"|"close", handler: fun(id: integer))sol.on(event: "activate", handler: fun(id: integer, why: "launch"|"request"))sol.on(event: "drop", handler: fun(id: integer, x: number, y: number))sol.on(event: "resize", handler: fun(id: integer, edge_x: number, edge_y: number, horizontal_side: "left"|"right"|nil, vertical_side: "top"|"bottom"|nil))sol.on(event: "scroll", handler: fun(dx: number, dy: number))sol.on(event: "click", handler: fun(x: number, y: number))sol.on(event: "surface", handler: fun(surface: string, action: string, data: any))sol.on(event: "direction", handler: fun(verb: "focus"|"move", dir: "left"|"right"|"up"|"down"))sol.on(event: "layout"|"monitors"|"restore", handler: fun())sol.on(event: "text_input", handler: fun(field: sol.TextField, why: "field"|"caret"|"framed"))sol.on(event: "keyboard", handler: fun(state: sol.KeyboardState, changed: "layout"|"caps"|"num"))
Parameters:
event:sol.Eventhandler:function
sol.log(message)
Write a line to the compositor’s log, at info level.
Parameters:
message:string
sol.decoration
The old name for sol.pane, from when a style was a single QML file. The same function.
sol.layout
The standard arrangements, from crates/layout. The pure ones take a count or a list and a sol.LayoutOptions and answer rects; tree and scroller make an arrangement the script holds.
sol.layout.master_stack(count, options)
One large window and the rest stacked beside it.
Parameters:
count:integeroptions:sol.LayoutOptions
Returns sol.Rect[].
sol.layout.tree()
A dwindle tree, held by the script that made it. Stateful because where a window lands depends on which window was split and where the pointer was.
Returns sol.Tree.
sol.layout.scroller(options)
A scrolling strip of columns, held by the script that made it.
Reads widths and default_width from the table given, which is meant to be config.scrolling; with none it uses the built-in widths.
Parameters:
options:{ widths?: number[], default_width?: integer }(optional)
Returns sol.Scroller.
sol.layout.strip(columns, options)
A row of columns, each holding a stack, with options.offset as how far the view has scrolled.
Parameters:
columns:sol.Column[]options:sol.LayoutOptions
Returns sol.Rect[].
sol.layout.strip_scroll_to(index, columns, options)
The view offset that brings column index (counting from 1) fully into view, moving as little as possible.
Parameters:
index:integercolumns:sol.Column[]options:sol.LayoutOptions
Returns number (offset).
sol.layout.scrolling(count, options)
A row of count windows at options.column of the width each, with options.offset as how far the view has scrolled.
Parameters:
count:integeroptions:sol.LayoutOptions
Returns sol.Rect[].
sol.layout.scroll_to(index, count, options)
The view offset that brings window index (counting from 1) of count fully into view in sol.layout.scrolling.
Parameters:
index:integercount:integeroptions:sol.LayoutOptions
Returns number (offset).
sol.layout.grid(sizes, options)
A grid of thumbnails, as close to square as the count allows. Each size keeps its aspect ratio inside its cell; overview uses this.
Parameters:
sizes:sol.Rect[]options:sol.LayoutOptions
Returns sol.Rect[].
A dwindle tree
A dwindle tree from sol.layout.tree(). Every call that takes options reads options.floors first, and lays the tree out in options as the work area.
Tree:insert(id, target, x, y, options)
Add a window by splitting target, or the tile under x, y when there is no target. Splits however small that leaves the halves.
Parameters:
id:integertarget:integer(optional)x:number(optional)y:number(optional)options:sol.LayoutOptions
Tree:insert_fitting(id, target, x, y, options)
insert, refusing a split that would leave a tile under options.minimum or a window under its floor: the same side first, then the other axis. Answers false and leaves the tree alone when neither fits.
Parameters:
id:integertarget:integer(optional)x:number(optional)y:number(optional)options:sol.LayoutOptions
Returns boolean (inserted).
Tree:insert_largest(id, options)
Split the largest tile that has room for another window at options.minimum, or answer false and leave the tree alone.
Parameters:
id:integeroptions:sol.LayoutOptions
Returns boolean (inserted).
Tree:remove(id)
Take a window out of the tree.
Parameters:
id:integer
Tree:resize(id, axis, by, options)
Move a seam from the keyboard: by is a signed share that grows the window when positive. The seam on the right or below is preferred, and the other one used only when there is none. Without options there is no minimum.
Parameters:
id:integeraxis:"width"|"height"by:numberoptions:sol.LayoutOptions(optional)
Tree:drag_seam(id, edge, edge_x, edge_y, options)
Move a seam from a drag: put the named side of the window at edge_x (for "left" and "right") or edge_y (for "top" and "bottom"). Hand it what the resize event gave you. Clamped to 0.05..0.95 and to options.minimum.
Parameters:
id:integeredge:"left"|"right"|"top"|"bottom"edge_x:numberedge_y:numberoptions:sol.LayoutOptions
Tree:contains(id)
Whether a window is in the tree.
Parameters:
id:integer
Returns boolean.
Tree:windows()
Every window in the tree, in tree order.
Returns integer[].
Tree:swap(a, b)
Trade the tiles of windows a and b and change nothing else: every split keeps its axis and its ratio, so a second swap puts it back.
Parameters:
a:integerb:integer
Returns boolean (both_were_in_the_tree).
Tree:layout(options)
Where every window in the tree goes, ready for sol.place.
Parameters:
options:sol.LayoutOptions
Returns sol.Slot[].
A scrolling strip
A strip of columns from sol.layout.scroller(). Every call that takes options reads options.floors first: a column is never laid out narrower than the widest floor in it.
Scroller:insert(id, options)
Open a window in a new column, to the right of the active one.
Parameters:
id:integeroptions:sol.LayoutOptions
Scroller:insert_into_column(id, options)
Add a window to the active column instead of beside it.
Parameters:
id:integeroptions:sol.LayoutOptions
Scroller:remove(id)
Take a window out of the strip.
Parameters:
id:integer
Scroller:focus_window(id, options)
Focus a window, bringing its column into view.
Parameters:
id:integeroptions:sol.LayoutOptions
Scroller:focus_sideways(by, options)
Move focus by columns left (negative) or right, taking the view with it.
Parameters:
by:integeroptions:sol.LayoutOptions
Scroller:focus_vertically(by)
Move focus by windows up (negative) or down within the active column.
Parameters:
by:integer
Scroller:move_vertically(by)
Swap the focused window with the one above it in its column (by is -1) or below it (1), carrying focus along. Nothing moves at the top or bottom.
Parameters:
by:integer
Returns boolean (moved).
Scroller:move_column(by, options)
Swap the active column with its neighbour by places away, carrying focus.
Parameters:
by:integeroptions:sol.LayoutOptions
Scroller:consume()
Pull the next column’s focused window into the active column.
Scroller:expel(options)
Push the focused window out into a column of its own.
Parameters:
options:sol.LayoutOptions
Scroller:move_to_column_of(id, target, options)
Move a window into the column another window is in.
Parameters:
id:integertarget:integeroptions:sol.LayoutOptions
Scroller:widen(id, by, options)
Widen (positive) or narrow the column a window is in, by a share of the view. Clamped so a column neither vanishes nor grows past the view.
Parameters:
id:integerby:numberoptions:sol.LayoutOptions
Scroller:cycle_width(options)
Cycle the active column through the preset widths.
Parameters:
options:sol.LayoutOptions
Scroller:configure(options)
Take widths and default_width from options as sol.layout.scroller does, keeping the columns, the focus and the view. For a strip sol.keep carried across a reload.
Parameters:
options:{ widths?: number[], default_width?: integer }
Scroller:scroll_by(delta)
Scroll the view by a distance, without moving focus.
Parameters:
delta:number
Scroller:focused()
The focused window, if there is one.
Returns integer|nil.
Scroller:contains(id)
Whether a window is in the strip.
Parameters:
id:integer
Returns boolean.
Scroller:windows()
Every window in the strip, left to right and top to bottom within a column.
Returns integer[].
Scroller:layout(options)
Where every window in the strip goes, ready for sol.place.
Parameters:
options:sol.LayoutOptions
Returns sol.Slot[].
Types
The shapes of the tables the calls above take and return.
sol.Rect
A rectangle in the one global coordinate space every monitor is a window onto, in logical pixels.
x:numbery:numberw:numberh:number
sol.Size
A size in logical pixels.
w:numberh:number
sol.Point
A point in the global coordinate space.
x:numbery:number
sol.Window
One row of sol.windows(): a window a layout may arrange, as it was at the moment the handler was called. x, y, w and h are where the window lives, frame included.
Has every field of sol.Rect, and:
id:integer— The window’s id, which every other call takes.title:string— The window’s title; the program’s name until its application arrives.focused:boolean— Whether it has the keyboard. While an item of a hosted scene holds the keyboard (Solium.keyboard.wants), the window it was taken from still readstrue, is drawn focused, and gets the keyboard back when the scene lets go.monitor:string— The name of the monitor it is on.modal:boolean— Whether it says it is a modal dialog.parent:integer|false(optional) — The window it belongs to;falsewhen the client named a parent that is not on screen, absent when it named none.leaving:boolean— Whether it is closing: betweenclosingandcloseorrefused.app_id:string— The application’s own name: a Waylandapp_id, an X11WM_CLASSclass. Empty until the application arrives.min:sol.Size(optional) — The smallest the application says it can be, frame included; absent when it set no limit.max:sol.Size(optional) — The largest the application says it can be, frame included; absent when it set no limit.cramped:boolean— Whether the layout last placed it withcramped = true.shown:boolean— Whether its application has shown its first frame yet.
sol.App
One row of sol.apps(): an installed, visible application.
id:string— The desktop file id, such as"org.mozilla.firefox".name:string— Its localised name.generic_name:string— Its localised generic name ("Web Browser"); empty when it named none.icon:string— A theme icon name, or an absolute path whenIcon=gave one.categories:string[]— ItsCategories=, split on;.
sol.Monitor
One row of sol.monitors(). x, y, w and h are the work area: the monitor less what layer-shell bars and hosted surfaces reserve.
Has every field of sol.Rect, and:
name:string— The connector name, such as"DP-1".scale:number— Device pixels per logical pixel.focused:boolean— Whether the pointer is on it.primary:boolean— Whether it is the primary monitor.transform:string— Its rotation, as Smithay names it in lower case:"normal","_90","_180","_270","flipped","flipped90","flipped180"or"flipped270". Not the spellingsol.monitors{ ... }takes.power:"on"|"off"— Whether its display is on. A monitor that is off keeps its place, its work area and its windows.whole:sol.Rect— The whole monitor, which is what a wallpaper or a fullscreen window covers.
sol.MonitorPlacement
How one monitor should be driven and where it goes, for sol.monitors{ ... }. Every key but name is optional. config.lua’s monitors section explains each one at length.
name:string— The connector name.solium --probelists the ones the machine has.x:integer(optional) — The top-left corner, in the global space.y:integer(optional) — The top-left corner, in the global space.right_of:string(optional) — Beside the monitor with this name.left_of:string(optional) — Beside the monitor with this name.above:string(optional) — Beside the monitor with this name.below:string(optional) — Beside the monitor with this name.align:"start"|"centre"|"end"(optional) — Which way the other axis lines up when placed beside a monitor of another size."centre"is the default.mode:string|{ w: integer, h: integer, refresh?: integer }(optional) —"2560x1440@165","2560x1440", or one of"best"(the default),"preferred"and"widest".vrr:boolean(optional) — Variable refresh rate, where the monitor and the driver offer it.transform:string|integer(optional) — Anticlockwise:"normal","90","180","270","flipped","flipped-90","flipped-180"or"flipped-270". A number is read as degrees.enabled:boolean(optional) —falsedoes not drive the monitor at all.primary:boolean(optional) — The monitor things belonging to one screen go on.scale:number|"auto"(optional) — Device pixels per logical pixel, from 0.5 to 8. Left out, it is worked out from the panel’s size.
sol.KeyboardState
The keyboard as sol.keyboard() answers.
layouts:string[]— The layout names, in order.active:integer— Which of them is live, counting from 1.layout_name:string— The live layout’s name, such as"Russian";""before the keymap is known. Notlayout, whichsol.keyboard{ ... }takes as the xkb names to compile, so a table read here can be handed back.layout_short:string— The live layout’s short name, such as"RU": theshortDescriptionxkb’s rules give it, upper-cased, or its first two letters.caps:boolean— Whether Caps Lock is on.num:boolean— Whether Num Lock is on.repeat_rate:integer— Repeats per second.repeat_delay:integer— Milliseconds a key is held before it repeats.
sol.TextField
The focused text field, as sol.text_input() answers: the window it is in, whether that window is framed, and its caret in the global space. The four numbers are there only once the application has said where its caret is, through text-input-v3; an application that never does has a field and no caret.
window:integer— The window’s id, assol.windows()gives it.framed:boolean— Whether the window is drawn in a frame of the compositor’s, or will be once its frame is built, so its pane style’s layers are drawn with it. False for a window drawn bare: fullscreen, one drawing its own decorations, every window under the pane style"none".x:number(optional) — The caret’s rectangle, where it is drawn: its window’s place and scale on screen are in it, presentation transforms included.y:number(optional)w:number(optional)h:number(optional)
sol.KeyboardOptions
What sol.keyboard{ ... } may change. Every key is optional and one left out is left as it is. The first five are xkb names; setting any of them compiles a new keymap, and one left blank falls back to the matching XKB_DEFAULT_* environment variable.
rules:string(optional)model:string(optional)layout:string(optional) — A comma-separated list, such as"us,ua".variant:string(optional) — One per layout, in the same order.options:string(optional) — xkb options, comma separated, such as"grp:alt_shift_toggle".repeat_rate:integer(optional) — Repeats per second.repeat_delay:integer(optional) — Milliseconds before repeating starts.active:integer(optional) — Which layout is live, counting from 1.caps:boolean(optional) — Caps Lock on or off. Done by pressing the keymap’s own Caps Lock key inside the compositor, so the window with the keyboard is told and the layout stays; a keymap with no such key cannot have it, and the log says so.num:boolean(optional) — Num Lock on or off, the same way.
sol.SurfaceOptions
A QML scene for sol.surface to draw.
scene:string— A file name looked up in~/.config/solium/qmland then in the shipped QML, or a path (absolute, or starting with~/).layer:sol.Layer(optional) — Which layer it is drawn in."background"is the default.on:"primary"|"every-monitor"|string|sol.Rect(optional) —"every-monitor"(the default) draws one instance per monitor, filling it;"primary"one on the primary monitor; a monitor’s name one there; a rect one at that rect.properties:table(optional) — Values for the scene’s properties, handed over as JSON: strings, numbers, booleans and tables of those, nested at most 64 deep and 65 536 values in all, a table counted once for each place it is in. A function, userdata or non-finite number is left out; a table nested deeper or holding more, or one that contains itself, is an error.interactive:boolean(optional) — Whether the pointer reaches it at all, so aGrabin a scene it does not reach holds nothing, and one it held when it is declared so is dismissed; nor does such a scene hold the keyboard. Where it does, the scene’s items decide which points are its (Solium.input), and the rest go to what is under it. An interactive scene sends actions withSolium.send(action, data), andsol.on("surface", ...)hears them.reserve:{ top?: integer, right?: integer, bottom?: integer, left?: integer }(optional) — Logical pixels taken out of the work area on those edges of every monitor the surface is on, whatever its size or placement; never negative. A scene’s ownSolium.surface.reserve.<edge>wins for an edge it sets. A change re-flows the windows once.outside_click:"swallow"|"pass"|table<string, "swallow"|"pass">(optional) — What a press outside an openGrabof the scene does once it has dismissed it: swallowed with its release (the default), or passed on to what is under it. A table names grabs, withdefaultfor the rest. Any other value fails the load.keyboard:{ bindings?: "except_claimed"|"all"|"none" }(optional) — While an item of the scene holds the keyboard (Solium.keyboard.wants):"except_claimed"(the default) keeps every binding but the keys the holding item claims (Solium.keyboard.claims),"all"keeps every binding, and"none"gives the scene every key but the Ctrl+Alt escapes. Any other value fails the load.
sol.Layer
"background"— Under everything, including client background surfaces."bottom"— Above the background, below windows."top"— Above windows, below client top surfaces, and below a fullscreen window that covers the top layer."overlay"— Above client top surfaces, below client overlay ones and the pointer.
sol.Easing
A curve by name, or four numbers: the control points of a cubic bezier, as CSS writes cubic-bezier(x1, y1, x2, y2). An unknown name is logged and the default kept.
"linear""outCubic""outBack""inOutQuad""inOutCubic""spring"number[]
sol.Motion
How long an animation takes and how it moves.
duration:integer(optional) — Milliseconds.easing:sol.Easing(optional)
sol.PresentOptions
Where and how sol.present draws a window. A key left out says nothing: with no rect the window is drawn at its own, with no opacity it is opaque, and with no rotation it is flat.
x:number(optional) — The rect it is drawn at: all four ofx,y,wandh, or none.y:number(optional)w:number(optional)h:number(optional)opacity:number(optional) — From 0 to 1.rotate_x:number(optional) — Degrees about the horizontal axis.rotate_y:number(optional) — Degrees about the vertical axis.rotate_z:number(optional) — Degrees in the plane of the screen.perspective:number(optional) — The viewer’s distance in pixels. Without it a rotation is orthographic.pivot_x:number(optional) — What the rotation turns about, as a share of the width. 0.5 is the default; outside 0..1 is kept.pivot_y:number(optional) — What the rotation turns about, as a share of the height. 0.5 is the default; outside 0..1 is kept.z:number(optional) — Draw order and nothing else: higher is in front, equal keeps the stacking order, 0 is the default. Input still follows the rect.deform:sol.Deform(optional) — A deformation of the window’s mesh.
sol.Deform
A deformation, named by effect. Its other keys are that effect’s parameters.
effect:"genie"— The effect, fromcrates/effects. An unknown one is logged and the window drawn undeformed.to:{ window: integer }|{ surface: string }|sol.Rect— What the window is pulled into or drawn out of. A window or a surface is followed as it moves; a rect stays where it is. A surface on several monitors is the one on the window’s own monitor.axis:"down"|"up"|"left"|"right"(optional) — For the genie: which edge leads.spread:number(optional) — For the genie: how much of the window is in motion at once. 0 is rigid.
sol.PlaceOptions
Where sol.place puts a window.
Has every field of sol.Rect, and:
tile:boolean|sol.Rect(optional) —true(the default) makes the rect a tile the window is held inside;falseplaces it without tiling it; a rect is the tile, for a window placed smaller than its tile.cramped:boolean(optional) — The layout saying the tile is smaller than the window’s own minimum.sol.windows()reports it back.
sol.Selection
What a selection holds, for sol.group. Every key is optional.
windows:integer[](optional) — Windows, by id.surfaces:string[](optional) — Surfaces, by the namesol.surfacegave them.monitors:string[](optional) — Everything drawn on these monitors.monitor:string(optional) — Which instance of each surface above: the one on this monitor.
sol.Shift
What sol.present_group does to a selection. x and y are a displacement, not a destination, and the rest compose with each member’s own transform.
x:number(optional)y:number(optional)opacity:number(optional)rotate_x:number(optional)rotate_y:number(optional)rotate_z:number(optional)perspective:number(optional)
sol.LoadingOptions
What a window does before its application arrives. Every key is optional and one left out keeps the compositor’s default.
scene:string(optional) — The QML scene: a name inqml/loading, the user’s first, or a path.patience:integer(optional) — Milliseconds to wait for an application that never arrives.reserves_a_slot:boolean(optional) — Whether the window takes its place in the layout at once.decorated:boolean(optional) — Whether its frame is drawn while it waits.fade:integer(optional) — Milliseconds the scene takes to fade off the application.
sol.IdleOptions
The idle blank. A key left out, or one that is not a number of 0 or more, keeps the default.
screens_off_after:number(optional) — Seconds with nobody at the machine before every screen is turned off. 0 never does.off_frame_interval:number(optional) — How often, in milliseconds, a window on a screen that is off is still told it may draw. 0 stops it.dbus_inhibit:boolean(optional) — Ownorg.freedesktop.ScreenSaver, so a browser’s D-Bus inhibitor keeps the screens on like a Wayland one. Default true.
sol.CursorOptions
The pointer’s theme. A key left out, or no table, means the configuration did not say, and XCURSOR_THEME and XCURSOR_SIZE have their turn.
theme:string(optional) — The name of an XCursor theme.size:integer(optional) — Logical pixels, from 8 to 256.scene:string(optional) — A QML scene to draw the pointer with, for every shape and ahead of the theme: a path,~expanded, or a name looked for in~/.config/solium/qmland then the shipped QML.SOLIUM_QML_CURSORoverrides it for one run.
sol.QmlOptions
How QML is rendered. Read once, before Qt starts.
renderer:"auto"|"gpu"|"software"(optional) —"auto"tries the GPU in a child process first.probe_timeout:integer(optional) — Milliseconds the GPU trial may take.
sol.SessionOptions
Telling systemd and D-Bus that Solium is the session. A key left out, or one of the wrong kind, keeps the default.
systemd:boolean(optional) — Export the environment to systemd and D-Bus activation and startsolium-session.target; undone on exit.trueby default.autostart:boolean(optional) — Startsolium-autostart.target, XDG autostart, beside it.trueby default.stop_timeout:integer(optional) — Milliseconds SIGTERM, SIGINT or SIGHUP waits for a clean stop before Solium ends at once. 5000 by default.
sol.DecorationEntry
One entry of sol.decorations().
name:string— Whatsol.panetakes.kind:"bundle"|"file"— A folder underqml/panes, or a single QML file underqml/decorations.
sol.LayoutOptions
What the layout calls take: the work area to arrange in, and how.
Has every field of sol.Rect, and:
gap:number(optional) — Space between windows and around the edge. 12 by default.ratio:number(optional) — The share of the width the main window takes inmaster_stack. 0.6 by default.column:number(optional) — The share of the width a column takes inscrolling. 0.5 by default.padding:number(optional) — Space around each thumbnail ingrid. 24 by default.split:number(optional) — Where a dwindle split falls, as a share of the tile divided. 0.5 by default.minimum:sol.Size(optional) — The smallest a dwindle tile may be. None by default.floors:table<integer,sol.Size>(optional) — Each window’s own floor, by id, frame included.offset:number(optional) — How far the view has scrolled, forstrip,strip_scroll_to,scrollingandscroll_to.
sol.Slot
A rect a tree or a strip hands back, tagged with its window.
Has every field of sol.Rect, and:
id:integercramped:boolean(optional) —trueon a tile smaller than its window’s own floor; trees only.
sol.Column
One column of a strip, for sol.layout.strip.
width:number(optional) — The column’s width as a share of the view. 0.5 by default.windows:integer(optional) — How many windows share the column. 1 by default.
sol.DeclaredWorkspace
What one workspace is, in a sol.workspaces declaration.
id:string— Unique within its group.name:string(optional) — What a shell shows; the id when left out.col:integer(optional) — Its column in the arrangement, from 1.row:integer(optional) — Its row in the arrangement, from 1.hidden:boolean(optional) — A workspace a shell should not list, such as a scratchpad.
sol.WorkspaceGroup
A group of workspaces that switch together: one per monitor, or one for every monitor.
id:stringmonitors:string[]— The monitors it is on, by connector name.showing:string[]— The ids it shows now.workspaces:sol.DeclaredWorkspace[]
sol.WorkspaceDeclaration
arrangement:{ kind?: string, columns?: integer, rows?: integer }(optional) — The shape the workspaces make, for a shell to draw.groups:sol.WorkspaceGroup[]windows:table<integer, string[]>(optional) — Which workspaces each window is on, by window id.
Internal
Tables the compositor keeps in sol for its own use. They are listed so the list is complete; a script should use the functions named.
sol._bindings— Internal: the bindings, by normalised combination. Usesol.bind,sol.boundandsol.unbind.sol._handlers— Internal: the listeners, by event. Usesol.on.sol._attempts— Internal: each attempt’sdone, by attempt id. Usesol.act.sol._keeps— Internal: whatsol.keepholds, by name.sol._strikes— Internal: how many times each listener ordonewas stopped, by function.sol._binding_sources— Internal: the notessol.bindandsol.unbindwere given, by combination.sol._unknown_settings— Internal: whatsol.unknownreported.