Repository: qtile/qtile

Issue:
Floating Windows Stutter/Lag when Moved/Resized
When I move/resize windows they lag/stutter really badly; sometimes if you really quickly either resize or move a floating window the whole wm will freeze for a couple seconds. This is especially prevalent with heavier programs for some reason. Also; somewhat unrelated; when resizing if the resized by the corner closest to your mouse instead of bottom right all the time it would feel a lot more natural.


Discussion:
On Sun, Apr 05, 2020 at 11:05:22AM -0700, simboid wrote:
> When I move/resize windows they lag/stutter really badly; sometimes if you really quickly either resize or move a floating window the whole wm will freeze for a couple seconds. This is especially prevalent with heavier programs for some reason. Also; somewhat unrelated; when resizing if the resized by the corner closest to your mouse instead of bottom right all the time it would feel a lot more natural.

Probably because we do an actual resize, vs. just drawing a box
showing where it would be resized to if you released right now.

If someone wanted to send a patch for this, that would be great :)

That being said IIRC other WM also do the actual resize and have much less lag. Could we optimize without losing the content, for example by drawing the window only once every ms or something in those lines?
I like what @ramnes is proposing, sometimes you resize based on how the program is rearranging its components (typically: a responsive page in a browser).
Implementing X sync extension protocol might help, e.g.: https://www.youtube.com/watch?v=js4UsY2m0wo

According to [a Reddit post](https://reddit.com/r/qtile/comments/shfdzr/buggy_movementresizing_any_ideas/hvbf0nz/), and from my understanding, very high mouse polling rate appears to be the cause of the issue. It'd be great if resizing rate could be adjusted.
> According to [a Reddit post](https://reddit.com/r/qtile/comments/shfdzr/buggy_movementresizing_any_ideas/hvbf0nz/), and from my understanding, very high mouse polling rate appears to be the cause of the issue. It'd be great if resizing rate could be adjusted.

I've had this exact same issue in BSPWM the fix in BSPWM was ```bspc config pointer_motion_interval 60``` however this made moving windows feel like a slideshow but was slightly better

Annoying because QTile is perfect besides this I blame XOrg for being garbage as always I long for the day Wayland just works on NVIDIA 😅
I suppose this wouldn't be trivial, but would a potential solution be to change it so that moving or resizing windows only draws the border until the window is set? Personally, I'd consider the constant redrawing of moving windows a form of eye-candy, so I think it better fits the scope of a lightweight tiling WM to be as responsive as possible under any circumstances.
Interesting idea - but I think (for x11) this would require work by the compositor rather than the WM. The borders are essentially attached to the window, rather than drawn manually by us.
Hmm, I just tested moving a window on my laptop (where I don't use picom), and there is no lag on windows I know to lag on my desktop (with picom). I wonder if there's a setting for picom that can fix the problem.
It could also be the polling rate of your laptop's trackpad - if it was less frequent than your desktop's mouse then there would be less frequency updates for the same movement distance 
Ah, yes I believe you're right, my desktop mouse is set to 1000hz, and I just found the other issue on that topic. I will say though, that the lag was noticeably improved when I disabled picom on my desktop, though not eliminated. I haven't found a way to achieve what I asked with picom yet, so I'd like to hear if anyone has ideas for workarounds for this issue, even if its "ugly".
I don't know how many times I have installed qtile to instantly uninstall it because of this issue. Thought it would be fixed by now, I guess there's no reason to move from awesome wm.

I use NVIDIA proprietary drivers on Arch Linux with a logitech mouse with 1000Hz polling rate with picom vsync enabled. So I guess I checked all the right boxes. This only happens on Qtile! Not on leftwm, awesome wm, i3, gnome, etc...

Resolving patch:
diff --git a/libqtile/backend/x11/core.py b/libqtile/backend/x11/core.py
--- a/libqtile/backend/x11/core.py
+++ b/libqtile/backend/x11/core.py
@@ -162,6 +162,9 @@ def __init__(self, display_name: str | None = None) -> None:
             | xcbq.PointerMotionHintMask
         )
 
+        # The last time we were handling a MotionNotify event
+        self._last_motion_time = 0
+
     @property
     def name(self):
         return "x11"
@@ -637,6 +640,12 @@ def handle_ButtonRelease(self, event) -> None:  # noqa: N802
     def handle_MotionNotify(self, event) -> None:  # noqa: N802
         assert self.qtile is not None
 
+        # Limit the motion notify events from happening too frequently
+        # Here we limit it to the config value
+        resize_fps = self.qtile.current_screen.x11_drag_polling_rate
+        if (event.time - self._last_motion_time) <= (1000 / resize_fps):
+            return
+        self._last_motion_time = event.time
         self.qtile.process_button_motion(event.event_x, event.event_y)
 
     def handle_ConfigureRequest(self, event):  # noqa: N802
diff --git a/libqtile/config.py b/libqtile/config.py
--- a/libqtile/config.py
+++ b/libqtile/config.py
@@ -369,6 +369,11 @@ class Screen(CommandObject):
     resized to fill it. If the mode is ``"stretch"``, the image is stretched to fit all
     of it into the screen.
 
+    The ``x11_drag_polling_rate`` parameter specifies the rate for drag events in the X11
+    backend. By default this is set to 120, but if you prefer it you can set it lower for
+    better performance or higher if you have a high refresh rate monitor. 120 would mean
+    that we handle a drag event 120 times per second.
+
     """
 
     group: _Group
@@ -382,6 +387,7 @@ def __init__(
         right: BarType | None = None,
         wallpaper: str | None = None,
         wallpaper_mode: str | None = None,
+        x11_drag_polling_rate: int = 120,
         x: int | None = None,
         y: int | None = None,
         width: int | None = None,
@@ -394,6 +400,7 @@ def __init__(
         self.right = right
         self.wallpaper = wallpaper
         self.wallpaper_mode = wallpaper_mode
+        self.x11_drag_polling_rate = x11_drag_polling_rate
         self.qtile: Qtile | None = None
         # x position of upper left corner can be > 0
         # if one screen is "right" of the other


Test patch: