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

Add support for notifying clients about pointer movements #1198

Merged
merged 1 commit into from
Mar 11, 2021

Conversation

lhchavez
Copy link
Contributor

@lhchavez lhchavez commented Feb 8, 2021

This change adds support for the VMware Mouse Position
pseudo-encoding[1], which is used to notify VNC clients when X11 clients
call XWarpPointer()[2]. This function is called by SDL[3] (and other
similar libraries) when they detect that the server does not support
native relative motion, like some RFB clients.

With this, RFB clients can choose to adjust the local cursor position
under certain circumstances to match what the server has set. For
instance, if pointer lock has been enabled on the client's machine and
the cursor is not being drawn locally, the local position of the cursor
is irrelevant, so the RFB client can use what the server sends as the
canonical absolute position of the cursor. This ultimately enables the
possibility of games (especially FPS games) to behave how users expect
(if the clients implement the corresponding change).

You can try this out end-to-end with noVNC with
novnc/noVNC#1520 applied!

Part of: #619

1: https://github.com/rfbproto/rfbproto/blob/master/rfbproto.rst#vmware-cursor-position-pseudo-encoding
2: https://tronche.com/gui/x/xlib/input/XWarpPointer.html
3: https://hg.libsdl.org/SDL/file/28e3b60e2131/src/events/SDL_mouse.c#l804

@lhchavez
Copy link
Contributor Author

lhchavez commented Feb 8, 2021

The failures were caused by Docker authentication limits. I swear I did not break the build ^^;;

lhchavez added a commit to lhchavez/noVNC that referenced this pull request Feb 8, 2021
This change adds the following:

a) A new button on the UI to enter full pointer lock mode, which invokes
   the Pointer Lock API[1] on the canvas, which hides the cursor and
   makes mouse events provide relative motion from the previous event
   (through `movementX` and `movementY`). These can be added to the
   previously-known mouse position to convert it back to an absolute
   position.
b) Adds support for the VMware Cursor Position pseudo-encoding[2], which
   servers can use when they make cursor position changes themselves.
   This is done by some APIs like SDL, when they detect that the client
   does not support relative mouse movement[3] and then "warp"[4] the
   cursor to the center of the window, to calculate the relative mouse
   motion themselves.
c) When the canvas is in pointer lock mode and the cursor is not being
   locally displayed, it updates the cursor position with the
   information that the server sends, since the actual position of the
   cursor does not matter locally anymore, since it's not visible.
d) Adds some tests for the above.

You can try this out end-to-end with TigerVNC with
TigerVNC/tigervnc#1198 applied!

Fixes: novnc#1493 under some circumstances (at least all SDL games would now
work).

1: https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API
2: https://github.com/rfbproto/rfbproto/blob/master/rfbproto.rst#vmware-cursor-position-pseudo-encoding
3: https://hg.libsdl.org/SDL/file/28e3b60e2131/src/events/SDL_mouse.c#l804
4: https://tronche.com/gui/x/xlib/input/XWarpPointer.html
lhchavez added a commit to lhchavez/noVNC that referenced this pull request Feb 8, 2021
This change adds the following:

a) A new button on the UI to enter full pointer lock mode, which invokes
   the Pointer Lock API[1] on the canvas, which hides the cursor and
   makes mouse events provide relative motion from the previous event
   (through `movementX` and `movementY`). These can be added to the
   previously-known mouse position to convert it back to an absolute
   position.
b) Adds support for the VMware Cursor Position pseudo-encoding[2], which
   servers can use when they make cursor position changes themselves.
   This is done by some APIs like SDL, when they detect that the client
   does not support relative mouse movement[3] and then "warp"[4] the
   cursor to the center of the window, to calculate the relative mouse
   motion themselves.
c) When the canvas is in pointer lock mode and the cursor is not being
   locally displayed, it updates the cursor position with the
   information that the server sends, since the actual position of the
   cursor does not matter locally anymore, since it's not visible.
d) Adds some tests for the above.

You can try this out end-to-end with TigerVNC with
TigerVNC/tigervnc#1198 applied!

Fixes: novnc#1493 under some circumstances (at least all SDL games would now
work).

1: https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API
2: https://github.com/rfbproto/rfbproto/blob/master/rfbproto.rst#vmware-cursor-position-pseudo-encoding
3: https://hg.libsdl.org/SDL/file/28e3b60e2131/src/events/SDL_mouse.c#l804
4: https://tronche.com/gui/x/xlib/input/XWarpPointer.html
lhchavez added a commit to lhchavez/noVNC that referenced this pull request Feb 8, 2021
This change adds the following:

a) A new button on the UI to enter full pointer lock mode, which invokes
   the Pointer Lock API[1] on the canvas, which hides the cursor and
   makes mouse events provide relative motion from the previous event
   (through `movementX` and `movementY`). These can be added to the
   previously-known mouse position to convert it back to an absolute
   position.
b) Adds support for the VMware Cursor Position pseudo-encoding[2], which
   servers can use when they make cursor position changes themselves.
   This is done by some APIs like SDL, when they detect that the client
   does not support relative mouse movement[3] and then "warp"[4] the
   cursor to the center of the window, to calculate the relative mouse
   motion themselves.
c) When the canvas is in pointer lock mode and the cursor is not being
   locally displayed, it updates the cursor position with the
   information that the server sends, since the actual position of the
   cursor does not matter locally anymore, since it's not visible.
d) Adds some tests for the above.

You can try this out end-to-end with TigerVNC with
TigerVNC/tigervnc#1198 applied!

Fixes: novnc#1493 under some circumstances (at least all SDL games would now
work).

1: https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API
2: https://github.com/rfbproto/rfbproto/blob/master/rfbproto.rst#vmware-cursor-position-pseudo-encoding
3: https://hg.libsdl.org/SDL/file/28e3b60e2131/src/events/SDL_mouse.c#l804
4: https://tronche.com/gui/x/xlib/input/XWarpPointer.html
@CendioOssman
Copy link
Member

Nice. I'll try to have a look and comment.

Have you also looked at adding support to our viewer? We prefer if features are fully implemented in both client and server so we can test properly without external projects.

@lhchavez
Copy link
Contributor Author

lhchavez commented Feb 8, 2021

Nice. I'll try to have a look and comment.

Have you also looked at adding support to our viewer? We prefer if features are fully implemented in both client and server so we can test properly without external projects.

not originally, but let me see how hard would it be.

@samhed samhed added the enhancement New feature or request label Feb 8, 2021
@lhchavez
Copy link
Contributor Author

lhchavez commented Feb 8, 2021

Nice. I'll try to have a look and comment.
Have you also looked at adding support to our viewer? We prefer if features are fully implemented in both client and server so we can test properly without external projects.

not originally, but let me see how hard would it be.

Turns out most of the functionality was already there! Used this little snippet of code[1] because fltk does not support relative mouse events and the cursor eventually hit a wall at the edges of the screen and broke everything unless we also mirrored the cursor-warping that was done remotely.

1: https://www.mail-archive.com/[email protected]/msg00023.html

Copy link
Member

@CendioOssman CendioOssman left a comment

Choose a reason for hiding this comment

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

This looks really nice. Good work!

Please also merge the two fix commits in too the others so we just have the two original commits. You can rebase and force push to fix up this.

How is this best tested? And what other servers and clients implement this?

Window rootwindow = DefaultRootWindow(fl_display);
XWarpPointer(fl_display, rootwindow, rootwindow, 0, 0, 0, 0,
pos.x, pos.y);
#endif
Copy link
Member

Choose a reason for hiding this comment

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

The coordinates here look wrong. The VNC screen might not be at coordinates 0,0, so everything here needs to be adjusted by an offset.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

haha, this is my first change of a client application, so i basically stumbled upon a solution that worked for me in this one case (translation: please suggest what offset should be added).

i kinda guess that the viewport->x/y() is what we want, but have no way of confirming since i don't know any situations other than the one i can test locally.

Copy link
Member

Choose a reason for hiding this comment

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

I see two test scenarios:

  1. Viewport doesn't match window. Just disable "Resize remote session..." in the options and make sure the session is larger or smaller than your client window
  2. Client is on different monitors. You need two monitors for this, and disable "Enable full-screen mode over all monitors"

I would suspect you need both viewport->x/y() and window()->x/y() to properly get screen coordinates.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

both cases seemed to work with a combination of x_root() + viewport->x(). thanks!

@lhchavez
Copy link
Contributor Author

lhchavez commented Mar 1, 2021

This looks really nice. Good work!

Please also merge the two fix commits in too the others so we just have the two original commits. You can rebase and force push to fix up this.

Done

How is this best tested? And what other servers and clients implement this?

So far only noVNC and this are the only open source server/clients that will implement this (after a quick Sourcegraph search). To test, run any SDL game that captures the cursor. In a pinch, running https://gist.github.com/lhchavez/50f4c31fc3faf811c3a572148479ef1b (requires Python3 and pygame) should do the trick.

if (!mouseGrabbed) {
// Do nothing if we do not have the mouse captured.
return;
}
Copy link
Member

Choose a reason for hiding this comment

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

We probably shouldn't do this if we aren't focused as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

wouldn't that make it hard to exit the client? in any case, moved to #1212.

Copy link
Member

Choose a reason for hiding this comment

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

How so? The warping should generally be in the way in such cases, not helping?

Window rootwindow = DefaultRootWindow(fl_display);
XWarpPointer(fl_display, rootwindow, rootwindow, 0, 0, 0, 0,
pos.x + x_root() + viewport->x(),
pos.y + y_root() + viewport->y());
Copy link
Member

Choose a reason for hiding this comment

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

Have you tried this with Wayland? It doesn't seem to work for me here. :/

Copy link
Contributor Author

@lhchavez lhchavez Mar 2, 2021

Choose a reason for hiding this comment

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

no, and i don't have a wayland environment :( is there a way to detect Wayland and not do this there? (in any case, moved to #1212).

@@ -51,6 +51,8 @@

#ifdef __APPLE__
#include "cocoa.h"
#include <CoreGraphics/CGGeometry.h>
#include <CoreGraphics/CGRemoteOperation.h>
Copy link
Member

Choose a reason for hiding this comment

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

The compiler can't find these here. I'm using an older SDK and gcc instead of clang. Hower #include <Carbon/Carbon.h> works for me. Does that work for you?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i don't have a macOS device ^^;; i was just fiddling until i got it working on CI. what's preferable in these cases? to drop support for macOS altogether? (in any case, moved to #1212).

Copy link
Member

Choose a reason for hiding this comment

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

It works for me with those changes, so go with that. :)

@CendioOssman
Copy link
Member

Doesn't seem to work on Windows either. macOS kind-a works, but there seems to be something generally wrong there unrelated to your changes.

@lhchavez
Copy link
Contributor Author

lhchavez commented Mar 2, 2021

Doesn't seem to work on Windows either. macOS kind-a works, but there seems to be something generally wrong there unrelated to your changes.

splitting this change into server-side (the one i really care about) and client-side, since it seems that the client-side will take more time.

@CendioOssman
Copy link
Member

We don't want you disappearing on us once the server is merged though, so I prefer the one PR. :)

@CendioOssman
Copy link
Member

The fix for macOS seems to be this at least:

diff --git a/vncviewer/DesktopWindow.cxx b/vncviewer/DesktopWindow.cxx
index bb51019e..385a9e88 100644
--- a/vncviewer/DesktopWindow.cxx
+++ b/vncviewer/DesktopWindow.cxx
@@ -188,6 +187,14 @@ DesktopWindow::DesktopWindow(int w, int h, const char *name,
 
   // Show hint about menu key
   Fl::add_timeout(0.5, menuOverlay, this);
+
+  // By default we get a slight delay when we warp the pointer, something
+  // we don't want or we'll get jerky movement
+#ifdef __APPLE__
+  CGEventSourceRef event = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState);
+  CGEventSourceSetLocalEventsSuppressionInterval(event, 0);
+  CFRelease(event);
+#endif
 }
 
 

@lhchavez
Copy link
Contributor Author

lhchavez commented Mar 2, 2021

We don't want you disappearing on us once the server is merged though, so I prefer the one PR. :)

don't worry about that, i promise right here and now to finish the whole thing. disappearing once the server is merged through will contribute to building a bad relationship with you, and I already have a lot of PRs open in repositories you own, so it's in my best interest to see through all of them (within reason: i'm probably not going to go buy a Mac just to test these changes ^^;; ).

but on the other hand, throw a man a bone! this is the part of the change that i have a vested interest in (and ultimately what motivates me to do the other parts). i see the vncviewer part as "building goodwill" (other less charitable folks call it the "open source contributor tax").

@lhchavez
Copy link
Contributor Author

lhchavez commented Mar 2, 2021

i have no idea why Travis is complaining about HOWTO.md O_O

@CendioOssman
Copy link
Member

i have no idea why Travis is complaining about HOWTO.md O_O

Unrelated and fixed on master now. :)

This change adds support for the VMware Mouse Position
pseudo-encoding[1], which is used to notify VNC clients when X11 clients
call `XWarpPointer()`[2]. This function is called by SDL (and other
similar libraries)  when they detect that the server does not support
native relative motion, like some RFB clients.

With this, RFB clients can choose to adjust the local cursor position
under certain circumstances to match what the server has set. For
instance, if pointer lock has been enabled on the client's machine and
the cursor is not being drawn locally, the local position of the cursor
is irrelevant, so the RFB client can use what the server sends as the
canonical absolute position of the cursor. This ultimately enables the
possibility of games (especially FPS games) to behave how users expect
(if the clients implement the corresponding change).

Part of: TigerVNC#619

1: https://github.com/rfbproto/rfbproto/blob/master/rfbproto.rst#vmware-cursor-position-pseudo-encoding
2: https://tronche.com/gui/x/xlib/input/XWarpPointer.html
3: https://hg.libsdl.org/SDL/file/28e3b60e2131/src/events/SDL_mouse.c#l804
@lhchavez
Copy link
Contributor Author

lhchavez commented Mar 2, 2021

i have no idea why Travis is complaining about HOWTO.md O_O

Unrelated and fixed on master now. :)

yay, thanks! i was stumped for a bit there as to how the other PR wasn't getting in the same situation.

@lhchavez
Copy link
Contributor Author

is this change also good to merge?

Copy link
Member

@CendioOssman CendioOssman left a comment

Choose a reason for hiding this comment

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

Yup. The only notes where about the client side of things.

@CendioOssman CendioOssman merged commit 41bf43b into TigerVNC:master Mar 11, 2021
@samhed
Copy link
Member

samhed commented Dec 1, 2021

For reference, the vncviewer side of this: #1212

aseering pushed a commit to aseering/noVNC that referenced this pull request Feb 20, 2023
This change adds the following:

a) A new button on the UI to enter full pointer lock mode, which invokes
   the Pointer Lock API[1] on the canvas, which hides the cursor and
   makes mouse events provide relative motion from the previous event
   (through `movementX` and `movementY`). These can be added to the
   previously-known mouse position to convert it back to an absolute
   position.
b) Adds support for the VMware Cursor Position pseudo-encoding[2], which
   servers can use when they make cursor position changes themselves.
   This is done by some APIs like SDL, when they detect that the client
   does not support relative mouse movement[3] and then "warp"[4] the
   cursor to the center of the window, to calculate the relative mouse
   motion themselves.
c) When the canvas is in pointer lock mode and the cursor is not being
   locally displayed, it updates the cursor position with the
   information that the server sends, since the actual position of the
   cursor does not matter locally anymore, since it's not visible.
d) Adds some tests for the above.

You can try this out end-to-end with TigerVNC with
TigerVNC/tigervnc#1198 applied!

Fixes: novnc#1493 under some circumstances (at least all SDL games would now
work).

1: https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API
2: https://github.com/rfbproto/rfbproto/blob/master/rfbproto.rst#vmware-cursor-position-pseudo-encoding
3: https://hg.libsdl.org/SDL/file/28e3b60e2131/src/events/SDL_mouse.c#l804
4: https://tronche.com/gui/x/xlib/input/XWarpPointer.html
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants