There are some artifacts with xr_glx_hybrid and it is not available as
experimental. Let's try glx with experimental backend. No tearing.
Tested with https://www.youtube.com/watch?v=MfL_JkcEFbE.
I think this is more correct. Dampen is used in network world, but
debounce seems more common (notably, we debounce push buttons).
Another word could be throttle but that does not match as we need to
react to the last event of a batch instead of the first (and we sure
don't want to drop the last).
This is cleaner than putting arbitrary attributes inside a task. The
scheduling will be done later, so it is safe to put put everything
inside the namespace object and avoid polluting the function
namespace.
We have a worker running as long as there is work. The optional sleep
is not implemented. There is a slight semantic difference: the work is
not postponed indefinitely.
Cancellation is asynchronous. So, there was a race condition where we
were throwing away the task we just scheduled. Don't really on
cancellation for synchronization. We also want to have only one
instance running. So, use locks to ensure only one instance is running
and only cancel running functions while in sleeping phase, otherwise,
let them run.
Currently, this OK, however, it is assumed the function has somehow
the same effect whatever the arguments we provide. This is true for
the two callbacks we use `@dampen` on.
There is some nasty bug we can run into:
```
ERROR: Task was destroyed but it is pending!
task: <Task pending name='Task-44' coro=<dampen.<locals>.decorator.<locals>.fn_now() running at /home/bernat/.config/i3/bin/i3-companion:115> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x7f687353e2b0>()]>>
```
This is often followed by a segfault. It's a bit difficult to
understand where it comes from. Sleeping a bit even if we don't want
to dampen seems to workaround this issue. It seems we have to keep a
reference to a task until it is cancelled properly.