Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is quick-tap to initiate window grab intended? #62

Closed
lambdadog opened this issue Apr 10, 2022 · 30 comments
Closed

Is quick-tap to initiate window grab intended? #62

lambdadog opened this issue Apr 10, 2022 · 30 comments

Comments

@lambdadog
Copy link
Contributor

lambdadog commented Apr 10, 2022

One thing that's been bugging me since I've started using Worm as my primary WM is occasionally when I click something on a window, it starts to drag the window around.

I eventually narrowed it down to specifically when I do a rapid single-click without moving my pointer, and after a bit of digging found the code that causes it:

discard self.dpy.XGrabPointer(client.frame.window, true, PointerMotionMask or
ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime)

Is this intended? If so, would it be possible to gate it behind a setting somewhere?

I've played with deleting the lines of code and it just makes using the WM feel way less "glitchy", as with it included it's far too easy for me to accidentally trigger a drag.

@lambdadog
Copy link
Contributor Author

At least with my trackpad, it's hard to even consistently trigger this intentionally, but it happens just enough unintentionally that it feels glitchy.

@codic12
Copy link
Owner

codic12 commented Apr 10, 2022

Hmm. This was actually intended to make it easier to be able to use the window border region (in addition to the title bar) to move and resize windows with left/right click. Not sure how I can work around this issue, sadly, because I can't reproduce it at all after a couple minutes of trying with both my mouse and touchpad (which was small by 2012 standards...). I think for now I can just add a gate to enable/disable this until I figure out a way to continue to use the window borders without triggering this touchpad issue.

Oh, also, this might affect Alt+Drag.

@lambdadog
Copy link
Contributor Author

lambdadog commented Apr 10, 2022

Oh, also, this might affect Alt+Drag.

It does, in fact. Would you mind explaining why? I'm still trying to figure out some of this Xlib stuff while hacking on worm.

Regarding reproducing it's possible your input devices don't allow fast enough clicks for this behavior I suppose? It's probably possible to produce a virtual click event that might allow reproducing, but I'm not familiar with the tooling one would use to do this.

I think for now I can just add a gate to enable/disable this until I figure out a way to continue to use the window borders without triggering this touchpad issue.

Ah, I didn't even notice this killed window borders moving/dragging :\

It's not something I use altogether too often thankfully, but that's definitely not ideal.

@codic12
Copy link
Owner

codic12 commented Apr 10, 2022

It does in fact. Would you mind explaining why? I'm still trying to figure out some of this Xlib stuff while hacking on worm.

When you grab the pointer (ButtonPress), your program takes exclusive ownership of the mouse until you ungrab it (ButtonRelease). If you don't grab the pointer in ButtonPress, then because I haven't asked X to do a passive grab for all Alt+mouse click, the MotionNotify and ButtonRelease events simply won't be reached, because X doesn't know to send it to us.

Since borders aren't afaik considered part of the window I genuinely have no idea why window borders can be dragged/resized in the first place 😅 some stuff is just too weird to understand... as long as it works! i'm no x guru

@lambdadog
Copy link
Contributor Author

Honestly border dragging/resizing is a bit goofy in the first place with how small even thicker borders are -- I imagine DEs must do some kind of "close enough" detection for their own edge/corner resizing behavior because it feels much easier, with much smaller borders.

@codic12
Copy link
Owner

codic12 commented Apr 10, 2022

Yes, it's called "alpha borders". They draw 2 borders; one the regular one, and one that is like 5 px but is transparent. That works well for those window managers because they can composite; but there's certainly ways to do it without a compositor (like xfwm4 does this fine without one). I do agree that it should be implemented better in worm.

@codic12
Copy link
Owner

codic12 commented Apr 10, 2022

Ok, I believe I figured out the issue, and the other way to implement this would likely lead to the same thing. (I may be wrong though, you can try the very very first version of worm which was in rust and didn't grab the pointer: https://github.com/codic12/worm/tree/f967b42aaf8be3886671d2d1aeb3b3e3c7ec1444 and see if you can reproduce there)

When you click that fast, the X server recieves both ButtonPress and ButtonRelease events, but it only gets a chance to process the first, so it behaves like it's stuck in ButtonPress mode.

@codic12
Copy link
Owner

codic12 commented Apr 10, 2022

If you can make do without Alt+Drag, then I could simply just add a feature-gate for this feature.

@lambdadog
Copy link
Contributor Author

lambdadog commented Apr 10, 2022

I may be wrong though, you can try the very very first version of worm which was in rust and didn't grab the pointer: https://github.com/codic12/worm/tree/f967b42aaf8be3886671d2d1aeb3b3e3c7ec1444 and see if you can reproduce there

I'll try this out as soon as I have the chance. I feel like I have used window managers with something akin to alt-drag before without issues before though.

That said, doing without border resizing is probably the most annoying part of disabling this, not going without Alt+Drag. It's not particularly feasible to make a window shorter in the vertical by resizing when it's near the top of the screen without first dragging the window down and often resizing "blind" since the bottom is now off screen.

@lambdadog
Copy link
Contributor Author

Actually the implementation in https://github.com/codic12/worm/tree/f967b42aaf8be3886671d2d1aeb3b3e3c7ec1444 doesn't cause this issue for me at all.

It does have another issue where if I move my mouse too quickly when Super+Dragging/Resizing near the edge of a window (and it is Super, rather than Alt), the window can end up "escaping" from my drag and I might end up dragging a window under it or just losing the drag entirely. Also, if you start the Alt-click on the background (either left or right click, oddly) and drag it onto a window, it snaps the center of the window to your cursor and you initiate a drag.

I assume those have more to do with the robustness of the solution than the methodology itself though?

@codic12
Copy link
Owner

codic12 commented Apr 10, 2022

The second one can probably be fixed without grabbing the pointer but I don't think the first one can. It's because worm doesn't actually have ownership of the pointer, so you can "lose" it to another window which wants control during a drag.

I have no idea what to do....

@lambdadog
Copy link
Contributor Author

It's probably not a massive issue as long as you keep regular click-drag/click-resize on the titlebar (and the borders, once a more robust border solution is implemented, I assume?)

This would mean that alt/super-clicking on the borders and titlebar would be grabbed as well (since all clicks there are grabbed), which conveniently covers all of the areas where you're liable to lose a drag by moving your pointer too fast with "un-owned dragging", which would come from alt/super-clicking on the window body?

That said I'm kind of confused why you can't first register a passive handler for alt/super-click, then grab the cursor when you actually receive the event from it?

@lambdadog
Copy link
Contributor Author

Oh, hm. In theory you're already doing that, aren't you? You're registering passive for any mod+click here

worm/src/wm.nim

Lines 39 to 57 in 76389ee

for button in [1'u8, 3]:
# list from sxhkd (Mod2Mask NumLock, Mod3Mask ScrollLock, LockMask CapsLock).
for mask in [
uint32 Mod1Mask, Mod1Mask or Mod2Mask, Mod1Mask or LockMask,
Mod1Mask or Mod3Mask, Mod1Mask or Mod2Mask or LockMask, Mod1Mask or
LockMask or Mod3Mask, Mod1Mask or Mod2Mask or Mod3Mask, Mod1Mask or
Mod2Mask or LockMask or Mod3Mask
]:
discard dpy.XGrabButton(
button,
mask,
root,
true,
ButtonPressMask or PointerMotionMask,
GrabModeAsync,
GrabModeAsync,
None,
None
)
then grabbing on receiving buttonpress

That leaves me back at being confused why my rapid clicks are even registering in the first place though -- I'm not holding any modifier when hitting them?

@codic12
Copy link
Owner

codic12 commented Apr 10, 2022

I think I have an idea (but again, I can't really test):

  • in the line you removed in the beginning (xgrabpointer) can you instead try to change both xgrabasync to xgrabsync?

@lambdadog
Copy link
Contributor Author

lambdadog commented Apr 10, 2022

  • in the line you removed in the beginning (xgrabpointer) can you instead try to change both xgrabasync to xgrabsync?

This causes worm to lock up immediately upon receiving any buttonpress event that reaches the line.

@lambdadog
Copy link
Contributor Author

By lock up I mean everything stops receiving mouse (and keyboard) events other than bindings implemented in the x server itself (I was still able to Ctrl+Alt+Fn to switch to a vtty)

@codic12
Copy link
Owner

codic12 commented Apr 10, 2022

ah yes. just grasping at straws by now (because I can't reproduce this, so I can't test!). sorry for all the "can you test this"s!
what if you change the line's confine_to parameter to the frame, so it reads like:

  discard self.dpy.XGrabPointer(client.frame.window, true, PointerMotionMask or
      ButtonReleaseMask, GrabModeAsync, GrabModeAsync, client.frame.window, None, CurrentTime)

(parameter after the 2nd grabmodeasync; None=>client.frame.window). I know this compiles and works just fine, but I don't know if it fixes the issue.

@lambdadog
Copy link
Contributor Author

I'll try that, but I have one or two theories of my own to test as well, so it might take a minute.

@lambdadog
Copy link
Contributor Author

It does not work.

@lambdadog
Copy link
Contributor Author

Wait, one moment, I feel like we might be looking at this incorrectly.

@lambdadog
Copy link
Contributor Author

The exact same thing as our "model of the bug" happens when I long-press and release on the window, which indicates to me something might be wrong with our model: worm receives a buttonpress event, then no button release event, yet I can move my cursor around freely without dragging.

@lambdadog
Copy link
Contributor Author

Check out #65. As far as I can tell, there's no negative effects to not capturing unmodified mouse1 buttonpress events on the window -- border dragging/resizing still works, alt-dragging/resizing does as well, and I couldn't find anything good worm was doing with it

@lambdadog
Copy link
Contributor Author

Nevermind, it broke refocusing 🤦‍♀️

@codic12
Copy link
Owner

codic12 commented Apr 10, 2022

Ok, so all those masks were to make sure that you could still drag the window even if you had caps lock, num lock, etc on. I thought you had to also have a null mask, but that's covered in the other possibilties I guess.

What do you mean by broke refocusing?

@lambdadog
Copy link
Contributor Author

What do you mean by broke refocusing?

Since you don't receive a buttonpress event upon clicking on the window (other than titlebar and border), you lose the functionality of raising and focusing the window if you click on it.

@codic12
Copy link
Owner

codic12 commented Apr 10, 2022

Ah yes, I believe that's why I added the 0 mask... I think I can just try using XSelectInput, which will do a passive grab instead, for that specific case.

@lambdadog
Copy link
Contributor Author

If that works then it might be the best solution?

@codic12
Copy link
Owner

codic12 commented Apr 10, 2022

Definitely (but I'm trying and now I can't get it to work, lol)

@codic12
Copy link
Owner

codic12 commented Apr 10, 2022

  discard self.dpy.XAllowEvents(AsyncPointer, CurrentTime)

does adding this after XGrabPointer in buttonpress.nim fix anything? (with upstream, not your PR)

@lambdadog
Copy link
Contributor Author

It does not, but I've got something that is working. It leaves a minor issue of "quick tapping on the titlebar starts a drag", so it's not the ideal fundamental solution, but it's a quickfix at least.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants