Skip to content
New issue

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

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

Already on GitHub? # to your account

implement locked cursor position hints #685

Merged
merged 7 commits into from
Oct 6, 2024
Merged

Conversation

sodiboo
Copy link
Contributor

@sodiboo sodiboo commented Sep 26, 2024

Firstly, this PR updates Smithay to latest master fixing stuff along the way (superseded by eb190e3) and also fixes a clippy lint in the second first commit.

The third second commit implements cursor position hints for locked pointers.

Clients which hold a locked pointer may send a zwp_locked_pointer_v1::set_cursor_position_hint which we currently ignore. This leads to annoying behaviour, detailed below.

This is based off a Smithay PR, and is going to be a draft until that PR is merged into Smithay:


I ran into this while working on an improved Wayland backend to fix some annoyances with the winit backend. One such thing is more seamless pointer handling; to properly support features such as warp-mouse-to-focus and internal pointer constraints, i must use the pointer constraints protocol in that backend. Since niri already has cursor compositing functionality baked in, it only makes sense to use this while niri has cursor focus. However, when the niri wayland backend loses cursor focus, the parent compositor (which is also niri, in my case) should know where the cursor actually is, because otherwise it can be really disorienting for a user.

Let's introduce some names to these branches of niri, and how to tell them apart in the videos i'm about to show you:

  • the latest commit from niri's main branch as i write and record this is 6ee5b5a. It is just after niri 0.1.9, and i have it installed as my main session. For the main graphical session, i have an orange Firewatch wallpaper.
  • the "location hint" / "position hint" branch of niri is the one in this PR. It is built off the latest commit from niri's main branch, and in particular sodiboo@b5bb92f is the one shown in these videos (which is based on sodiboo/smithay@0739c87). This is not my main session; i ran it as a standalone instance of niri on a separate TTY. As such, it has a modulated wallpaper to tell it apart.
  • the Wayland backend is what i've been mostly working on. It is based on a version of niri just before 0.1.9, and in particular sodiboo@20e9767 is the one i've been using to test the pointer hint functionality. For the purposes of this PR, this is just a Wayland client. It also has the same modulated wallpaper, because it's not the graphical login session.

The Wayland backend, aka the client shown in these videos, is correctly reporting its cursor position hint. It is the same client in both videos, and sending the same requests in both. The only difference is what the compositor (Yeah! I know the client is a compositor too, but to be clear i mean the outermost one) does with the position hint. It's worth noting that this cursor lock is held to allow the client to deal with its own internal pointer constraints smoothly. To ensure a seamless windowing experience, however, the lock is automatically released when the cursor touches the window edge. The intended result is that it's not obvious there is a cursor lock to begin with, but features like warp-mouse-to-focus should still "just work". Currently, in the winit backend, that doesn't work at all.


The latest commit of niri's main branch (shown in orange) simply ignores it. This leads to a disorienting mess where the cursor seems to jump around erratically. When the pointer lock is deactivated, the pointer is returned to its initial position. Because of how this client in particular behaves, you need to go several roundtrips over a window before you can pass through it, but in other clients there may not even be a guarantee that the cursor can make progress. Because the compositor ignores cursor position hints, this leads to worst-case possibility that a surface can (unintentionally!) become an impenetrable barrier for the cursor. That's not very cool.

no-pointer-location-hint.mp4

However, the position hint variant from this PR (shown in blue) properly updates the pointer location hint whenever it receives a cursor position hint. Below is shown the same client; which hides the cursor and renders its own, and sends cursor position hints. The client is running the exact same commit of my Wayland backend for niri, but with the compositor supporting cursor position hints, it is now much more seamless. If you weren't paying attention really, you might think that the nested niri actually doesn't at all render a cursor, and this is just the outer cursor moving unimpeded across the surface. You'd be wrong.

with-pointer-location-hint.mp4

@YaLTeR
Copy link
Owner

YaLTeR commented Sep 27, 2024

Haven't read through this yet, but FYI I'll be merging this Smithay update PR: #230

@sodiboo
Copy link
Contributor Author

sodiboo commented Sep 27, 2024

FYI I'll be merging this Smithay update PR: #230

That's okay, this can be rebased once that is merged.

@sodiboo sodiboo force-pushed the position-hint branch 2 times, most recently from 6739cb3 to 3aaab48 Compare September 27, 2024 17:40
@sodiboo
Copy link
Contributor Author

sodiboo commented Sep 27, 2024

The Smithay PR was merged. I've updated this branch to no longer use my fork of Smithay. As far as i'm concerned, this can be merged as-is, and i'm already running this branch on my main system (precisely to fix the annoyance with the client i'm working on shown in the videos above), feel free to review it or block merging prior to the other PR and i'll rebase this to include just the third commit

@sodiboo sodiboo marked this pull request as ready for review September 27, 2024 17:53
@sodiboo sodiboo changed the title Update Smithay and implement locked cursor position hints ~~Update Smithay and~~ implement locked cursor position hints Oct 2, 2024
@sodiboo sodiboo changed the title ~~Update Smithay and~~ implement locked cursor position hints implement locked cursor position hints Oct 2, 2024
@sodiboo
Copy link
Contributor Author

sodiboo commented Oct 2, 2024

Since the Smithay changes are now merged separately into the main branch, i've dropped that first commit from this PR. I've kept the commit fixing a lint.

The next commit implements cursor position hints naively, in such a way that a window on an output edge may cause the cursor to cross a monitor boundary or even end up out of bounds. This makes no sense for niri, because cursor position hints are surface-relative, and in niri, surfaces are positioned within each output and not in a global coordinate space.

The newest commit fixes that issue, by constraining the cursor position hint to the current monitor's bounds. Previously, if the cursor went outside of all monitor boundaries (which is easy if the surface extends past the output edge), the cursor would just teleport back to the middle of the layout. Now, it works seamlessly, and the cursor is just snapped back to the same edge of a screen that it left through, once the client releases their pointer lock (or it otherwise is inactivated, such as by sending a position hint outside of their surface, which is blindly respected, but it can no longer cross a monitor boundary)

It would be really cool to have some kind of scrolling mechanism in niri to make sure the cursor is still visible even if it is semantically off-screen[1] (just because we clip the cursor position hint doesn't mean the client cares, it might render a cursor off-screen, which is unintuitive), i have not implemented such a thing as it would require a deep dive into the layout code that i don't want to deal with. I'm also not going to implement such a mechanism for this PR, and i don't currently have plans to do anything further on this PR.

  1. see also: add pointer location hint Smithay/smithay#1551 (comment)

This PR is ready to merge, as far as i'm concerned.

@YaLTeR
Copy link
Owner

YaLTeR commented Oct 2, 2024

It would be really cool to have some kind of scrolling mechanism in niri to make sure the cursor is still visible even if it is semantically off-screen[1]

(sidenote i've had this extremely cursed idea where the cursor always remains in the center of the monitor, and moving the mouse moves the whole rendering around it)

@sodiboo
Copy link
Contributor Author

sodiboo commented Oct 2, 2024

It would be really cool to have some kind of scrolling mechanism in niri to make sure the cursor is still visible even if it is semantically off-screen[1]

(sidenote i've had this extremely cursed idea where the cursor always remains in the center of the monitor, and moving the mouse moves the whole rendering around it)

I've had the same cursed idea, actually, but I I'm pretty sure I decided to keep my mouth shut once I realized that this would just be extremely disorienting and painful to use. I think it's more reasonable and much less motion-sickness-inducing if we enforce this constraint but with the horizontal struts instead of a line down the center of the output.

@sodiboo
Copy link
Contributor Author

sodiboo commented Oct 2, 2024

unrelated, but it was brought up in the Smithay matrix that my implementation for Anvil (which was based on the one in niri) doesn't work for fractional scaling. and yeah, that seems reasonable. this PR probably needs to account for fractional scaling, and it should be tested prior to merging. also note that my Wayland backend (referenced above as my personal motivation for implementing this in the first place) does not currently do fractional scaling, so go find some other client to test with.

Copy link
Owner

@YaLTeR YaLTeR left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I applied some fixes, including the things I commented on. This should be good to merge. Could you test it once more?

unrelated, but it was brought up in the Smithay matrix that my implementation for Anvil (which was based on the one in niri) doesn't work for fractional scaling

Really? Seems fine here. I tested it and it seems to work fine too.

});
pointer.set_location(target);
} else {
error!("cursor_position_hint called on a surface that is not the focused surface, but the constraint is active. this should be impossible.");
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For future reference, rustfmt doesn't break apart long string literals on its own, so this might just cause it to prevent formatting for the whole block. There's probably an unstable flag for that but I always kinda did it by hand.

output_geometry.size -= (1, 1).into();
(origin + location).constrain(output_geometry.to_f64())
});
pointer.set_location(target);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the window moves between set_cursor_position_hint and unlocking? Blender sets the position hint right in front of unlocking, but I don't see why that should always be the case. Either way, I guess it's not a big deal.

if focused_surface == surface {
let target = self
.niri
.output_for_root(surface)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be a subsurface, so this needs to get the root surface first.

error!("cursor_position_hint called on a surface that is not the focused surface, but the constraint is active. this should be impossible.");
}
} else {
error!("cursor_position_hint called with no focused surface, but the constraint is active. this should be impossible.");
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two error conditions are actually possible. At the moment, the niri pointer focus does not take pointer grabs into account (this pointer focus business is Complicated, fairly sure last time I looked into it I figured I need API in Smithay to get the active pointer grab data, but I never got around to adding it yet). Which means that you can start middle dragging in blender, then touchpad-swipe the window away, and the niri pointer focus will point at either nothing or another window, but the real Smithay pointer focus will still be on Blender (due to a click pointer grab).

output_geometry.size -= (1, 1).into();
(origin + location).constrain(output_geometry.to_f64())
});
pointer.set_location(target);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs a queue redraw if the cursor was visible.

@sodiboo
Copy link
Contributor Author

sodiboo commented Oct 6, 2024

unrelated, but it was brought up in the Smithay matrix that my implementation for Anvil (which was based on the one in niri) doesn't work for fractional scaling

Really? Seems fine here. I tested it and it seems to work fine too.

Hm, yeah. It does in fact seem to work just fine here. Note that i hadn't tested it with any scaling at all (only 1.0 as that's the scale i use on my main desktop); but i just wrote a hellish script that pingpongs my scale on a centigrade precision, and while it was definitely headache-inducing to look at, the fractional scaling position hints do seem to work just fine, actually.

It's worth noting that the complaint i saw was about an entirely different compositor; but it was also with a very similar implementation to what i did here, and given that i don't mention scaling at all in this PR diff, i did expect the issue to be present in both, if any.

I applied some fixes, including the things I commented on. This should be good to merge. Could you test it once more?

Yeah. It seems to work great! LGTM.

@YaLTeR YaLTeR merged commit 66be000 into YaLTeR:main Oct 6, 2024
10 checks passed
@YaLTeR
Copy link
Owner

YaLTeR commented Oct 6, 2024

Thanks

FluxTape pushed a commit to FluxTape/niri that referenced this pull request Oct 15, 2024
* implement cursor position hints

* Remove redundant fully qualified path

* Find root surface

* Convert nesting to if-return

* Manually wrap error messages

* Remove error!() prints

* Add queue redraw

---------

Co-authored-by: Ivan Molodetskikh <yalterz@gmail.com>
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants