Please fill out the following:
Scroll Version:
1.12.21
Description:
There has recently been work done in the codebase to prevent animations from "queuing" if multiple commands are sent together. I have noticed this behavior still remains if commands are executed through a LUA script. For example, here's a script I use to cycle sizes based on monitor layout:
local scroll = require("scroll")
-- cycle_size_smart.lua
local args, state = ...
local direction = args[1] -- "prev" or "next"
local invert = args[2] == "true" -- whether to use opposite axis
local workspace = scroll.focused_workspace()
if workspace then
local layout_type = scroll.workspace_get_layout_type(workspace)
-- In vertical layout, we cycle widths (h)
-- In horizontal layout, we cycle heights (v)
local axis = (layout_type == "vertical") and "v" or "h"
-- If invert is true, use the opposite axis
if invert then
axis = (axis == "h") and "v" or "h"
end
local command = string.format("cycle_size %s %s", axis, direction)
scroll.command(nil, command)
end
I use this script in my config as:
bindsym --inhibited $mod+u lua $scripts/smart_cycle_size.lua prev false
bindsym --inhibited $mod+i lua $scripts/smart_cycle_size.lua next false
bindsym --inhibited $mod+Shift+u lua $scripts/smart_cycle_size.lua prev true
bindsym --inhibited $mod+Shift+i lua $scripts/smart_cycle_size.lua next true
Steps to Reproduce
- Apply the configuration and script provided above to Scroll.
- Enable an animation, preferably one with a long duration to better see the bug.
- Send consecutive, rapid keybinds, i.e. four quick
mod+u keystrokes in a row.
- Animations are queued instead of interrupted based on keypress.
(Disclaimer: I used LLM to assist the following root cause analysis + rough implementation plan. It contains information retrieved by an LLM, but it has been reviewed by me prior to posting.)
Root cause
The direct IPC path and the Lua binding path commit resize transactions differently:
scrollmsg cycle_size ... runs the command in ipc_client_handle_command().
Its outer transaction_commit_dirty() runs after execute_command() has
restored server.delay_transaction, so it both collects and commits the
dirty layout nodes.
- The keybindings launch
scrollmsg lua .... The Lua script calls
scroll.command(nil, command), which nests another execute_command().
execute_command() sets server.delay_transaction = true; the nested
scroll.command() calls transaction_commit_dirty() while that flag is
still true. The dirty nodes are moved into server.pending_transaction, but
the commit is deferred.
- When the nested command returns, the outer IPC handler calls
transaction_commit_dirty() again. Because the dirty-node list is now
empty, _transaction_commit_dirty() returns immediately and does not flush
the already-created pending transaction.
animation_animate() eventually calls transaction_commit_delayed() when
the current animation finishes, so the next resize starts only then.
Relevant files to this bug
sway/commands.c
sway/lua.c
sway/desktop/transaction.c
sway/desktop/animation.c
Implementation Suggestion
- Make the transaction commit boundary handle an existing pending transaction
when an explicit, non-delayed commit occurs after dirty nodes were already
collected by a nested command.
- Preserve the recent behavior that client-only transactions remain deferred
while an animation is active; only an explicit command transaction should
force the pending resize through.
- Add a regression test covering repeated
cycle_size commands through a Lua
script invoked over IPC, alongside the existing animation transaction tests.
- Validate that direct
scrollmsg cycle_size behavior remains unchanged and that
repeated Lua-triggered resizes start the incoming animation immediately.
Please fill out the following:
Scroll Version:
1.12.21
Description:
There has recently been work done in the codebase to prevent animations from "queuing" if multiple commands are sent together. I have noticed this behavior still remains if commands are executed through a LUA script. For example, here's a script I use to cycle sizes based on monitor layout:
I use this script in my config as:
Steps to Reproduce
mod+ukeystrokes in a row.(Disclaimer: I used LLM to assist the following root cause analysis + rough implementation plan. It contains information retrieved by an LLM, but it has been reviewed by me prior to posting.)
Root cause
The direct IPC path and the Lua binding path commit resize transactions differently:
scrollmsg cycle_size ...runs the command inipc_client_handle_command().Its outer
transaction_commit_dirty()runs afterexecute_command()hasrestored
server.delay_transaction, so it both collects and commits thedirty layout nodes.
scrollmsg lua .... The Lua script callsscroll.command(nil, command), which nests anotherexecute_command().execute_command()setsserver.delay_transaction = true; the nestedscroll.command()callstransaction_commit_dirty()while that flag isstill true. The dirty nodes are moved into
server.pending_transaction, butthe commit is deferred.
transaction_commit_dirty()again. Because the dirty-node list is nowempty,
_transaction_commit_dirty()returns immediately and does not flushthe already-created pending transaction.
animation_animate()eventually callstransaction_commit_delayed()whenthe current animation finishes, so the next resize starts only then.
Relevant files to this bug
sway/commands.csway/lua.csway/desktop/transaction.csway/desktop/animation.cImplementation Suggestion
when an explicit, non-delayed commit occurs after dirty nodes were already
collected by a nested command.
while an animation is active; only an explicit command transaction should
force the pending resize through.
cycle_sizecommands through a Luascript invoked over IPC, alongside the existing animation transaction tests.
scrollmsg cycle_sizebehavior remains unchanged and thatrepeated Lua-triggered resizes start the incoming animation immediately.