Bug #1604 zaphod heads/full-screen problem

9 views
Skip to first unread message

HASM

unread,
Mar 1, 2023, 9:17:39 PM3/1/23
to TigerVNC Developer Discussion
Please refer to bug #1604 (https://github.com/TigerVNC/tigervnc/issues/1604) to make some sense of the stuff below.

I've compiled vncviewer from fedora's tigervnc-1.13.0-4.f37.x86_64 src rpm, with the following additions in DesktopWindow.cxx.

Added a system_handler event at construction of the first DesktopWindow, which is removed at destruction of the last DesktopWindow.

The handler always return 0, i.e does not handle anything, but vlogs the X11 event name, serial, display and window, the fields in a XAnyEvent structure. Could add more fields depending on the event type, but didn't go there yet.

At the beginning of fltkDispatch, added vlogging of FL event and window.

Starting vncviewer not in full mode, when I move the mouse out of vncviewer's window and into the other zaphod head, there is a X11 LeaveNotify event, followed by a FL LEAVE event with a NULL window, followed by a X11 FocusOut event, and then a FL_UNFOCUS event. This probably all happens before the cursor moves across screens.

Starting vncviewer in full mode, when I move the mouse out of vncviewer's window and into the other zaphod head, there is a X11 LeaveNotify event, followed by a X11 GenericEvent (not sure this is there all the time), followed by a FL LEAVE event with a NULL window, but no X11 FocusOut event or FL_UNFOCUS. As the mouse is not released, events generated on the other screen are still sent to the vnc window on its screen.

Another funny thing is that I use a virtual desktop manager, and can, with actions on the "other screen", page out of the page where vncviewer is showing, and things are still stuck.

There's lots of other events in either case, but that's the major difference as far as I can tell.

Guess I need to start digging inside fltk.

-- Henrique

Pierre Ossman

unread,
Mar 2, 2023, 1:07:18 AM3/2/23
to HASM, TigerVNC Developer Discussion
On 3/2/23 03:17, HASM wrote:
>
> Starting vncviewer in full mode, when I move the mouse out of vncviewer's window and into the other zaphod head, there is a X11 LeaveNotify event, followed by a X11 GenericEvent (not sure this is there all the time), followed by a FL LEAVE event with a NULL window, but no X11 FocusOut event or FL_UNFOCUS. As the mouse is not released, events generated on the other screen are still sent to the vnc window on its screen.
>

What are the coordinates on these events? That might be a source of the
problem, where the coordinates are root-relative, so vncviewer thinks
the cursor is still over its own window.

Regards
--
Pierre Ossman Software Development
Cendio AB https://cendio.com
Teknikringen 8 https://twitter.com/ThinLinc
583 30 Linköping https://facebook.com/ThinLinc
Phone: +46-13-214600

A: Because it messes up the order in which people normally read text.
Q: Why is top-posting such a bad thing?

HASM

unread,
Mar 2, 2023, 7:53:46 AM3/2/23
to TigerVNC Developer Discussion
HASM> Starting vncviewer in full mode, when I move the mouse out of
HASM> vncviewer's window and into the other zaphod head, there is a X11
HASM> LeaveNotify event, followed by a X11 GenericEvent (not sure this is
HASM> there all the time), followed by a FL LEAVE event with a NULL
HASM> window, but no X11 FocusOut event or FL_UNFOCUS. As the mouse is
HASM> not released, events generated on the other screen are still sent to
HASM> the vnc window on its screen.

PO> What are the coordinates on these events? That might be a source of
PO> the problem, where the coordinates are root-relative, so vncviewer
PO> thinks the cursor is still over its own window.

I have the whole dump of fields from the diferent xevent structure for all cases the system handler sees (on my limited test runs), with the exception of the time field, when this exists.

Can attach the whole sequence if needed, but it is quite long and probably uninteresting.

Here is the small section around the leave (crossing) event:

DesktopWindow: GenericEvent serial=2197 send_event=0, display=0x558e95923660 window=25769803907
DesktopWindow: GenericEvent serial=2197 send_event=0, display=0x558e95923660 window=25769803907
DesktopWindow: MotionNotify serial=2198 send_event=1 display=0x558e95923660 window=83886185 root=3384 subwindow=0 x=2 y=215 x_root=2 y_root=215, state=0 is_hint=
DesktopWindow: FL_MOVE 0x558e95a7f1a0
DesktopWindow: GenericEvent serial=2198 send_event=0, display=0x558e95923660 window=25769803907
DesktopWindow: LeaveNotify serial=2198 send_event=0 display=0x558e95923660 window=83886185 root=3382 subwindow=0 x=0 y=0 x_root=1913 y_root=215, modee=0 detail=3 same_screen=0, focus=1 state=0
DesktopWindow: GenericEvent serial=2198 send_event=0, display=0x558e95923660 window=25769803907
DesktopWindow: FL_LEAVE (nil)
DesktopWindow: GenericEvent serial=2198 send_event=0, display=0x558e95923660 window=25769803907
DesktopWindow: MotionNotify serial=2199 send_event=1 display=0x558e95923660 window=83886185 root=3382 subwindow=0 x=0 y=0 x_root=1913 y_root=215, state=0 is_hint=
DesktopWindow: FL_MOVE 0x558e95a7f1a0

- The LeaveNotify event has a different root and same_screen is false.
- No Focus related events are generated.
- Subsequent MotionNotify events have that different root, but are caught and acted on regardless.

I need to look at the fltk event loop to understand what is going on. I've never looked at fltk code until last week, and barely looked at any tigervnc code before.

VNCviewer could probably trap this leave event and release the grab, but not sure this is the correct place to catch this.

-- Henrique

HASM

unread,
Mar 5, 2023, 12:43:45 PM3/5/23
to TigerVNC Developer Discussion, Pierre Ossman
> What are the coordinates on these events? That might be a source of
> the problem, where the coordinates are root-relative, so vncviewer
> thinks the cursor is still over its own window.

It is indeed the source of the problem.

If I start in a non-zaphod configuration, this is the
sequence of events:

-----------------------------------------------------------------
DesktopWindow: intercept_handler: type: MotionNotify
DesktopWindow: serial: 1663
DesktopWindow: send_event: True
DesktopWindow: display: 0x16f33a0
DesktopWindow: window: 27263078
DesktopWindow: root: 1731
DesktopWindow: subwindow: 0
DesktopWindow: x: 2
DesktopWindow: y: 200
DesktopWindow: x_root: 1922
DesktopWindow: y_root: 200
DesktopWindow: state: 0
DesktopWindow: is_hint:
DesktopWindow: fltkDispatch: event: FL_MOVE win: 0x1847d80
DesktopWindow: handle: event: FL_MOVE
DesktopWindow: intercept_handler: type: GenericEvent
DesktopWindow: serial: 1663
DesktopWindow: send_event: False
DesktopWindow: display: 0x16f33a0
DesktopWindow: extension: XInputExtension
DesktopWindow: evtype: 6
DesktopWindow: intercept_handler: type: LeaveNotify
DesktopWindow: serial: 1663
DesktopWindow: send_event: False
DesktopWindow: display: 0x16f33a0
DesktopWindow: window: 27263078
DesktopWindow: root: 1731
DesktopWindow: subwindow: 0
DesktopWindow: x: -4
DesktopWindow: y: 200
DesktopWindow: x_root: 1916
DesktopWindow: y_root: 200
DesktopWindow: mode: NotifyNormal
DesktopWindow: detail: NotifyAncestor
DesktopWindow: same screen: True
DesktopWindow: focus: True
DesktopWindow: state: 0
DesktopWindow: intercept_handler: type: GenericEvent
DesktopWindow: serial: 1663
DesktopWindow: send_event: False
DesktopWindow: display: 0x16f33a0
DesktopWindow: extension: XInputExtension
DesktopWindow: evtype: 6
DesktopWindow: fltkDispatch: event: FL_LEAVE win: (nil)
DesktopWindow: handle: event: FL_LEAVE
DesktopWindow: ungrabPointer: ungrabbing mouse
-----------------------------------------------------------------

The LeaveNotify event makes its way to DesktopWindow::handle,
where in the FL_LEAVE case, this is executed:

-----------------------------------------------------------------
if (mouseGrabbed) {
if ((Fl::event_x() < 0) || (Fl::event_x() >= w()) ||
(Fl::event_y() < 0) || (Fl::event_y() >= h())) {
ungrabPointer();
}
}
-----------------------------------------------------------------

and the mouse let go.

In the zaphod configuration, this is the sequence:

-----------------------------------------------------------------
DesktopWindow: intercept_handler: type: MotionNotify
DesktopWindow: serial: 1750
DesktopWindow: send_event: True
DesktopWindow: display: 0xf4e3a0
DesktopWindow: window: 60817513
DesktopWindow: root: 3384
DesktopWindow: subwindow: 0
DesktopWindow: x: 3
DesktopWindow: y: 306
DesktopWindow: x_root: 3
DesktopWindow: y_root: 306
DesktopWindow: state: 0
DesktopWindow: is_hint:
DesktopWindow: fltkDispatch: event: FL_MOVE win: 0x10abee0
DesktopWindow: handle: event: FL_MOVE
DesktopWindow: intercept_handler: type: GenericEvent
DesktopWindow: serial: 1750
DesktopWindow: send_event: False
DesktopWindow: display: 0xf4e3a0
DesktopWindow: extension: XInputExtension
DesktopWindow: evtype: 6
DesktopWindow: intercept_handler: type: LeaveNotify
DesktopWindow: serial: 1750
DesktopWindow: send_event: False
DesktopWindow: display: 0xf4e3a0
DesktopWindow: window: 60817513
DesktopWindow: root: 3382
DesktopWindow: subwindow: 0
DesktopWindow: x: 0
DesktopWindow: y: 0
DesktopWindow: x_root: 1906
DesktopWindow: y_root: 306
DesktopWindow: mode: NotifyNormal
DesktopWindow: detail: NotifyNonlinear
DesktopWindow: same screen: False
DesktopWindow: focus: True
DesktopWindow: state: 0
DesktopWindow: intercept_handler: type: GenericEvent
DesktopWindow: serial: 1750
DesktopWindow: send_event: False
DesktopWindow: display: 0xf4e3a0
DesktopWindow: extension: XInputExtension
DesktopWindow: evtype: 6
DesktopWindow: fltkDispatch: event: FL_LEAVE win: (nil)
DesktopWindow: handle: event: FL_LEAVE
-----------------------------------------------------------------

which is not followed by this
DesktopWindow: ungrabPointer: ungrabbing mouse

as x=0 and y=0, i.e. not less than 0, nor greater than
width/height, and thus the mouse is not let go.

Note the root coordinates, they are 1906 and 306, which
happen to be the coordinates of the mouse on the other
screen. I moved from the right zaphod head to the left
zaphod head, thus the mouse is close to the right edge of
that head, which has geometry 1920x1080.

This is confirmed by the root field. On the non-zaphod case
the root screen is always the same, 1731, in the zaphod
case, the first MotionNotify I show, and all previous ones,
have a root window of 3384 and, the leave event that ought
to trigger the mouse ungrab is on root 3382. I don't think
FLTK provides a way to get the root window.

Before the mouse moves out of the zaphod head, as vncviewer
is in full window mode, x==x_root and y==y_root. After that
x_root and y_root seem correct in the other head, but
x==y==0, always.

A "simple" solution that works, IF AND ONLY IF, all monitors
have the same resolution, is to replace the check in the
DesktopWindow::handle, FL_LEAVE case, with:

-----------------------------------------------------------------
if (mouseGrabbed) {
if ((Fl::event_x() < 0) || (Fl::event_x() >= w()) ||
(Fl::event_y() < 0) || (Fl::event_y() >= h()) ||
((Fl::event_x_root() - Fl::event_x()) % Fl::w()) ||
((Fl::event_y_root() - Fl::event_y()) % Fl::h())) {
ungrabPointer();
}
}
-----------------------------------------------------------------

Works like a charm. It's Sunday morning PST, I have a bunch
of house related stuff to do. I'll try to work a generic
solution, though maybe someone on this mailing list could
come up with it much faster than me :-)

-- Henrique

Pierre Ossman

unread,
Mar 8, 2023, 1:41:58 AM3/8/23
to HASM, TigerVNC Developer Discussion
On 05/03/2023 18:43, HASM wrote:
>
> A "simple" solution that works, IF AND ONLY IF, all monitors
> have the same resolution, is to replace the check in the
> DesktopWindow::handle, FL_LEAVE case, with:
>

That's a massive if, so unfortunately that is not going to cut it.

How about something like this:

diff --git a/vncviewer/DesktopWindow.cxx b/vncviewer/DesktopWindow.cxx
index c5952094..b85485b2 100644
--- a/vncviewer/DesktopWindow.cxx
+++ b/vncviewer/DesktopWindow.cxx
@@ -844,12 +844,20 @@ int DesktopWindow::handle(int event)
case FL_LEAVE:
case FL_DRAG:
case FL_MOVE:
- // We don't get FL_LEAVE with a grabbed pointer, so check manually
if (mouseGrabbed) {
+ // We don't get FL_LEAVE with a grabbed pointer, so check manually
if ((Fl::event_x() < 0) || (Fl::event_x() >= w()) ||
(Fl::event_y() < 0) || (Fl::event_y() >= h())) {
ungrabPointer();
}
+ // We also don't get sensible coordinates on zaphod setups
+#if !defined(WIN32) && !defined(__APPLE__)
+ if ((fl_xevent != NULL) && (fl_xevent->type == MotionNotify) &&
+ (((XMotionEvent*)fl_xevent)->root !=
+ XRootWindow(fl_display, fl_screen))) {
+ ungrabPointer();
+ }
+#endif
}
if (fullscreen_active()) {
// calculate width of "edge" regions

This looks at motion events. If that doesn't work, then looking at the
leave events might.

Regards
--
Pierre Ossman Software Development
Cendio AB http://cendio.com
Teknikringen 8 http://twitter.com/ThinLinc
583 30 Linköping http://facebook.com/ThinLinc
Reply all
Reply to author
Forward
0 new messages