After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 587714 - error in way gdk_event_request_motions() works
error in way gdk_event_request_motions() works
Status: RESOLVED OBSOLETE
Product: gtk+
Classification: Platform
Component: Backend: X11
2.17.x
Other Linux
: Normal normal
: 3.0
Assigned To: gtk-bugs
gtk-bugs
Depends on:
Blocks: motion-event-tracker
 
 
Reported: 2009-07-03 22:05 UTC by Stanislav Brabec
Modified: 2018-02-10 03:38 UTC
See Also:
GNOME target: ---
GNOME version: 2.15/2.16


Attachments
motion-feedback.c (9.03 KB, text/x-csrc)
2009-07-07 15:57 UTC, Stanislav Brabec
  Details
motion-feedback-mainloop.c (13.15 KB, text/x-csrc)
2010-02-08 09:37 UTC, Stanislav Brabec
  Details
Patch to implement gdk_device_get_position() (11.91 KB, patch)
2010-02-13 21:30 UTC, Benjamin Moody
none Details | Review
Patch to deprecate gdk_event_request_motions() (2.92 KB, patch)
2010-02-13 21:33 UTC, Benjamin Moody
none Details | Review

Description Stanislav Brabec 2009-07-03 22:05:38 UTC
Using the recommended way of processing of motion event hints with gdk_event_request_motions() and without gdk_window_get_pointer() there is a problem to render a smooth feedback based on hints. It seems to be a problem of the philosophy of the implementation - after a quick stopping of move, the visual feedback is delayed.

How to reproduce:

0. Use a recommended implementation of motion hint processing.

{  // motion_event handler
  if (motion_event->is_hint)
  {
    x = motion_event->x;
    y = motion_event->y;
    ; // handle (x,y) motion
  }
  gdk_event_request_motions (motion_event); // handles is_hint events
}

1. Press mouse button and start dragging

2. Move quickly to a new place and stop movement just on place

3. Don't release the mouse button

Actual result:

There is no clean way to implement smooth motion feedback:

- Last hint event is very distant from the actual location of the mouse.

- The temporary last event does not have a hint flag.

- Processing all events, not only hints can cause uncomfortable "sticky pointer experience".

- Only deprecated use of gdk_window_get_pointer() can help to guess, that the event actually processed may be the the last event in the queue and that the feedback should be rendered even if it is does not have a hint.


Proposed fix:

Check the motion event queue. If there are no events in queue any more, set hint for the last event.

or

Update documentation and recommend use of gdk_window_get_pointer().

Note:

The deprecated way described in the bug 573154 has a a smoother visual feedback than the correct implementation.
Comment 1 Stanislav Brabec 2009-07-07 11:47:17 UTC
I had to go deeply into X server sources and documentation to check, what is happening here.

When hint mask is set and pinter motion happens, a motion hint event is sent. According to the X server documentation, X server is free to not send any further events, unless XQueryPointer (called by gdk_window_get_pointer, gdk_device_request_motions, gdk_device_get_state) or XGetMotionEvents (called by gdk_device_get_history) are called.

When processing a visual feedback on a slow device, it is optimal to process the latest known position of the pointer. This pointer query will also order new hint delivery.

But gdk_event_request_motions() works in other way: Read pointer position and ignore it. It will allow delivery of a new hint, but if hint will not appear, it skips of previous motion.

GTK+ documentation does not tell this cleanly. In particular, in code "if (event->is_hint)", the else part is never called with GDK_POINTER_MOTION_HINT_MASK.


Now suppose that you have a slow rendering. Let's inspect all possible motion feedback implementations with GDK_POINTER_MOTION_HINT_MASK:


1. Using gdk_window_get_pointer at the beginning.

Broken according to the documentation and bug 573154, as it does not work with non-core pointer. Anyway, it provides the most smooth feedback.

Offset behind pointer during motion: ~1 frame rendering time

Offset behind pointer during non-motion: 0


2. Using gdk_window_request_motions at the end.

Recommended by documentation, but broken by design - it looses all event that came during the event processing. It cause problem: If no more events appear in some time period, it feedback uses bad position (not the last one).

Offset behind pointer during motion: ~1 frame rendering time

Offset behind pointer during non-motion: ~1 frame rendering time


3. Using gdk_window_request_motions at the beginning.

Not recommended, broken by design, but often used in gtk+ code. New hint may appear early in the old event processing, potentially available newer pointer positions are lost. It may also cause bad position described above.

Offset behind pointer during motion: ~2 frame rendering time

Offset behind pointer during non-motion: ~0.5 frame rendering time, sometimes ~2 frame rendering time



Possible solutions of the problem:

gdk_window_request_motions() seems to be broken by design. You should process a hint with updated mouse position. When you update mouse position, X server should reset a hint and no call of gdk_window_request_motions should be needed.

There is several possibilities to solve this problem:

Change gdk_window_request_motions to NOP and always update event->x and event->y before calling callback.

If you consider longer delay in feedback sufficient, then you can recommend use of event->x and event->y coordinates and gdk_window_request_motions can generate more motion events marked as non-hint. Partially breaks current API behavior.

The above plus recommend to ignore motion hint coordinates. Just call gdk_window_request_motions to process hint. But such program will work only on fixed GTK+!

Implement new gdk_window_get_pointer_for_event working correctly for non-core events and recommend it for processing hints. Deprecate gdk_window_request_motions.
Comment 2 Stanislav Brabec 2009-07-07 15:57:49 UTC
Created attachment 137978 [details]
motion-feedback.c

Demonstration program that tests all possible implementations of motion feedback.

As you can see and test them, none of them works as expected.
Comment 3 Stanislav Brabec 2009-07-07 19:55:18 UTC
Well, things are even worse.

Even GDK_POINTER_MOTION_HINT_MASK doesn't work properly in on my home machine (openSUSE 11.0, Athlon 1200, 32 bit, serial mouse using "inputattach --intellimouse /dev/ttyS1" and ATI Remote Wonder (both configured to send core events), ATI Rage 128, xorg-x11-7.3). All three callbacks behave in a following incorrect way:

- If mouse pointer moves less than about 10 pixels per second, all (or nearly all) events are sent. Feedback is delaying.

- If mouse pointer moves more than about 10 pixels per second, motion hints work as expected.

It may be a bug of X server, but it may be caused by a race caused by unwanted calling of above mentioned X functions (XQueryPointer, XGetMotionEvents) inside GDK.
Comment 4 Michael Natterer 2009-07-15 13:40:41 UTC
what makes you think that this:

  if (motion_event->is_hint)
  {
    x = motion_event->x;
    y = motion_event->y;
    ; // handle (x,y) motion
  }
  gdk_event_request_motions (motion_event); // handles is_hint events

is the recommended way of motion event processing? You should rather
not look at "is_hint" at all and simply process all events.

Did you try that and does it work as expected?
Comment 5 Stanislav Brabec 2009-07-15 14:03:05 UTC
Sorry, this is a recommended way, and this way is used in the utility in comment 2:

gdk_event_request_motions()
{ 
  /* motion_event handler */
  x = motion_event->x;
  y = motion_event->y;
  /* handle (x,y) motion */
  gdk_event_request_motions (motion_event); /* handles is_hint events */
}

But the code in comment #4 does exactly the same: With  GDK_POINTER_MOTION_HINT_MASK set, all X servers I tested generate only events with ->is_hint == TRUE.

The real problem is elsewhere:

If GDK_POINTER_MOTION_HINT_MASK is set, X server does not send motion events, but only hints.

You have to call XQueryPointer() or XGetMotionEvents() to get new event. It happens e. g. by calling gdk_event_request_motions().

But gdk_event_request_motions() does not update pointer position and does not issue any event.

If you stop motion before gdk_event_request_motions() is called, but after entering the event processing, your application will never update the mouse position.

You can easily demonstrace this problem in the test application in comment 2.
Comment 6 Benjamin Moody 2010-02-06 05:36:05 UTC
I've never been quite sure what the "right" way is to use motion hints.  But basically, you have to figure that by the time you process a hint event, the coordinates specified in that event are already out of date.

So, if you want your display to be synchronized with the pointer, you need to query the pointer, and base whatever you're drawing on the response to that query, not the hint event coordinates.

(If you wanted to, I suppose you could *also* draw something based on the hint coordinates, before querying the pointer, but this only makes sense if drawing is substantially faster than a round-trip to the server.)

So gdk_event_request_motions() is fundamentally broken, except in the one unusual case where you simply want to be notified that the pointer has moved, and you don't care where.

From what I can tell, the function to use is gdk_device_get_state().  It's a little more complicated than gdk_window_get_pointer(), but I think the following function should do the trick.  This is similar, I think, to the "gdk_window_get_pointer_for_event()" function that Stanislav Brabec mentioned in comment #1 (and it might indeed be useful for GDK to provide this function as a convenience; I'd probably call it "gdk_window_get_device_coordinates()" or something like that.)

/* Retrieve pointer coordinates for a particular input device. */
void get_device_pointer(GdkWindow *win, GdkDevice *dev,
                        gdouble *x, gdouble *y, GdkModifierType *mask)
{
  gdouble *axes;
  int i;

  axes = g_new(gdouble, dev->num_axes);
  gdk_device_get_state(dev, win, axes, mask);

  for (i = 0; i < dev->num_axes; i++) {
    if (x && dev->axes[i].use == GDK_AXIS_X)
      *x = axes[i];
    else if (y && dev->axes[i].use == GDK_AXIS_Y)
      *y = axes[i];
  }

  g_free(axes);
}

Then, in your motion-notify event handler, you'd do this:

gboolean motion_notify(GtkWidget *w, GdkEventMotion *ev, gpointer data)
{
  if (ev->is_hint)
    get_device_pointer(ev->window, ev->device, &ev->x, &ev->y, &ev->state);

  ...
}

Assuming this works (folks should test it with multiple-input-device systems; I don't have one to test at the moment), then I think it should be mentioned in the documentation as the preferred way of handling motion hint events.

So much for how to fix applications.  As for fixing GDK itself, it might make sense, as mentioned in comment #1, for gdk_event_request_motions() to synthesize another (non-hint) event containing the correct coordinates.  This would, I think, improve matters somewhat for existing applications (and the function of gdk_event_request_motions() would actually reflect its name.)  However, I don't think this is a good long-term solution (programs would be drawing stuff twice for every motion hint, plus programs written and tested using the new GDK would then fail mysteriously on the old GDK), so I think gdk_event_request_motions() should be deprecated in any case.
Comment 7 Stanislav Brabec 2010-02-06 16:40:04 UTC
Well, there is a consequence of calling gdk_device_get_state(), at least for X backend - it "unlocks" device for the next hint generating. It may be an unwanted side effect for a program, that does the main rendering in the main loop. (Next hint comes too early after the previous.)

I have been thinking about it again, and I think, that gdk_event_request_motions() could remain, but it should change its behavior and add extra hint processing code to the GTK+ core. And yes, a function that correctly updates position is needed as well.

Imagine an application that finishes the feedback in the main loop. It would want next event long time after finishing the motion hint callback itself finishes.

I think about implementation that will:

- Introduce new internal switch - hint_event_suppress.

- Introduce new code to the event processing that would be capable to not send some events to the application.

It would require some thinking to make it working:

- Never trash last event.

- If hints are required, then prevent queueing of events.

- Minimize position delay (possibly update position before sending events to application).

- Do not break code that uses motion hints in combination with gdk_device_get_history() (Such code is useful for drawing programs that may draw all in once or draw by steps.)

- Make it working for backends that support motion hints (X) and backends that don't.
Comment 8 Benjamin Moody 2010-02-06 20:40:56 UTC
(In reply to comment #7)

Yes, gdk_device_get_state() should "unlock" the device (just as gdk_window_get_pointer() does for the core pointer) and cause it to send more hint events.  Isn't that the point of the exercise?

(By the way, gdk_device_get_state() is what gdk_event_request_motions() does - the latter just throws away the result.)

It's true that those events will be sent sooner than they would be with the current, broken, recommended use of gdk_event_request_motions().  But since (as I said above) the coordinates in a hint event are always out of date, this doesn't really make any difference: when the next hint event comes in, you'll call gdk_device_get_state() again and retrieve the actual coordinates.

As far as processing stuff in the main loop goes: Are you suggesting that the program would not do any drawing until the server stops sending events?  That would cause problems regardless of what you're doing.

To put it another way: from the server's perspective, there's no difference between doing this:

gboolean motion_event(GtkWidget *w, GdkEventMotion *ev)
{
    x = ev->x;
    y = ev->y;
    gtk_widget_queue_draw(w);
    gdk_event_process_motions(ev);
}

and doing this:

gboolean motion_event(GtkWidget *w, GdkEventMotion *ev)
{
    if (ev->is_hint)
        gdk_device_get_state(...);
    x = ev->x;
    y = ev->y;
    gtk_widget_queue_draw(w);
}

In both cases, the function (a) queries the pointer, thus implicitly requesting more hint events, and (b) notifies GTK+ that some drawing needs to be done.  In both cases, there is the possibility of a new hint event being sent immediately after this function finishes and before any drawing is done.  The only difference is that in the first case, "x" and "y" are going to be forever out of date.

If, instead, you do all the drawing within the motion event handler, then it doesn't make any difference if more events arrive during that time.

I suppose it could potentially be useful to do some special processing before sending events to the widget (e.g., when a hint event comes in, change it into a non-hint event by calling gdk_device_get_state().)  But if so, this should be done by GTK+, not GDK, and it should be done on a per-widget basis.
Comment 9 Stanislav Brabec 2010-02-06 21:56:23 UTC
One of main ideas of motion hint in X was: Use lowest possible level to drop events that application cannot process anyway.

> Are you suggesting that the program would not do any drawing until the server stops sending events?

No, I am proposing reversely (guessing that exactly this was intended by X designers): Stop sending events until application completes drawing.

If we will be able to do this, updating of the mouse position on the beginning of the feedback may be (nearly) obsolete. The hint will be sent in a moment when the system is idle and ready to process the hint and will very probably contain the latest known position.

I think that in both examples motion hint lock should be released after finishing of queued drawing. (If there was any event in time between, motion hint should be generated immediately.)

Well, the idea "when a hint event comes in, change it into a non-hint" is bad. When hint comes, it means: There was a motion after processing the last reported position => hint must not be lost. (But it could be postponed by the toolkit, or overwritten by the next event (if backend does not support motion hints).)

> I suppose it could potentially be useful to do some special processing before
sending events to the widget

Maybe. But it is not so easy. X have two functions to get next hint. One reads actual position, second reads the whole motion history. Both release motion hint lock. If you don't know, whether feedback is interested in the whole history or only actual position, you must always read and keep the whole history.

My rough idea of possibilities (the are non-exclusive, all of them can be implemented):

1. Fix gdk_event_request_motions() and call it after finishing of all motion feedback actions.
+ simple to implement on X level
- feedback delay may appear in the main loop

2. Implement and call gdk_event_request_motions_on_idle() or gdk_event_request_motions_on(priority) during application initialization and don't care in the callback.
+ trivial use in applications
+ still easy to implement
- feedback delay may appear

3. Call gdk_device_get_state() on beginning of the feedback.
+ always smallest possible delay
- position needs to be correctly updated on the beginning of the feedback

4. Call gdk_device_get_history() on beginning of the feedback.
+ The only way to manipulate with the whole motion history.
Comment 10 Benjamin Moody 2010-02-07 02:18:43 UTC
(In reply to comment #9)

I think we're not understanding each other here...

> One of main ideas of motion hint in X was: Use lowest possible level to drop
> events that application cannot process anyway.
> 
> > Are you suggesting that the program would not do any drawing until the server stops sending events?
> 
> No, I am proposing reversely (guessing that exactly this was intended by X
> designers): Stop sending events until application completes drawing.

Well, you can't stop the server from sending events.  Stop the application from listening to events, yes.  As far as I know, that is what GTK+ does.

> If we will be able to do this, updating of the mouse position on the beginning
> of the feedback may be (nearly) obsolete. The hint will be sent in a moment
> when the system is idle and ready to process the hint and will very probably
> contain the latest known position.

That is the theory behind what is currently being done (with the officially-recommended use of gdk_event_get_motions().)  It's wrong, because the mouse may have stopped moving while you were busy drawing stuff.

"Very probably" isn't good enough, as your example program demonstrates.

> I think that in both examples motion hint lock should be released after
> finishing of queued drawing. (If there was any event in time between, motion
> hint should be generated immediately.)

Can't be done.  That's not the way X works.  The way X works is that a motion hint is sent the first time the mouse moves *after calling XQueryPointer*.

Let me explain it a different way: say you are drawing a cursor that's supposed to follow the mouse pointer.  You get a message saying that the mouse is at coordinates (x0, y0), so you draw the cursor there.  Now, what you want is to be notified *the next time the mouse moves to somewhere other than (x0, y0)*.  The question of whether the mouse has moved *since the time you finished drawing* is not particularly of interest.

> Well, the idea "when a hint event comes in, change it into a non-hint" is bad.

I'm not saying this should always be done.  It might, however, be useful in a lot of cases, to simplify widget implementations.

By the way, in case it wasn't clear, I did not mean simply "always set the is_hint flag to zero."  I meant "if the is_hint flag is set, then query the pointer and update the coordinates in the event before passing it to the widget."  In other words, the widget's motion-notify handler could be written exactly the same way regardless of whether motion hints are being used.

Again, I'm suggesting this as an optional behavior of GTK+ to simplify widget implementations (and only for those widgets that request it), not as a core GDK feature.

> > I suppose it could potentially be useful to do some special processing before
> sending events to the widget
> 
> Maybe. But it is not so easy. X have two functions to get next hint. One reads
> actual position, second reads the whole motion history. Both release motion
> hint lock. If you don't know, whether feedback is interested in the whole
> history or only actual position, you must always read and keep the whole
> history.

That's why it should not be done at the GDK level.

But this is not really an important point.

> My rough idea of possibilities (the are non-exclusive, all of them can be
> implemented):
> 
> 1. Fix gdk_event_request_motions() and call it after finishing of all motion
> feedback actions.
> + simple to implement on X level
> - feedback delay may appear in the main loop

What do you mean by "fix gdk_event_request_motions()"?  If you mean that gdk_event_request_motions() would synthesize a motion event, my objections are (as I said above):
1. for older programs, feedback would be slower overall (every other frame would be drawn with out-of-date coordinates)
2. newer programs written and tested with the fixed GDK would fail mysteriously when run with the older GDK
For both reasons, I still say gdk_event_request_motions() should be deprecated.

> 2. Implement and call gdk_event_request_motions_on_idle() or
> gdk_event_request_motions_on(priority) during application initialization and
> don't care in the callback.
> + trivial use in applications
> + still easy to implement
> - feedback delay may appear

What does this mean?  Could you elaborate on what those functions would do?

> 3. Call gdk_device_get_state() on beginning of the feedback.
> + always smallest possible delay
> - position needs to be correctly updated on the beginning of the feedback

This is what I am recommending (that this should be done by the widget implementation.)  I'm not sure what you mean by "position needs to be correctly updated"; could you elaborate?  I don't see that this is any more of a burden for app/widget authors than calling gdk_event_request_motions() is.

> 4. Call gdk_device_get_history() on beginning of the feedback.
> + The only way to manipulate with the whole motion history.

Yes, for programs that want to use hints, but still need the full motion history, this needs to remain as an option.  For other programs, however, it would slow things down.  Thus, again, my feeling that this should *not* be done at the GDK level at all, but at the widget or application level.
Comment 11 Stanislav Brabec 2010-02-07 22:23:59 UTC
> What do you mean by "fix gdk_event_request_motions()"?  If you mean that
> gdk_event_request_motions() would synthesize a motion event, my objections are
> (as I said above):

Yes, I have been thinking about synthesizing of motion event.

> 1. for older programs, feedback would be slower overall (every other frame
would be drawn with out-of-date coordinates)

If it will be used properly (end of all actions), then there will be only minimal delay:
- gdk_event_request_motions() is called, position is updated
- feedback finishes in a few machine cycles
- main loop starts to process next event in a few machine cycles
- the old position should not be more than few microseconds old
But it is only my theory.

> 2. newer programs written and tested with the fixed GDK would fail mysteriously
> when run with the older GDK

Not fail mysteriously. Just sometimes the motion feedback will stop earlier than it should. (exactly as it does just now)

> For both reasons, I still say gdk_event_request_motions() should be deprecated.

Thinking about it again, yes, I agree. gdk_event_request_motions() only complicates things with no benefit.

>> 4. Call gdk_device_get_history() on beginning of the feedback.
>> + The only way to manipulate with the whole motion history.
> Thus, again, my feeling that this should *not* be done
> at the GDK level at all, but at the widget or application level.

Yes, I agree. But GDK must be aware of the fact, that any call of XQueryPointer() erases the X motion history and would make such feedback impossible on application level.

gdk_event_request_motions_on_idle():

My idea was adding an implicit call of gdk_event_request_motions() when GLib enters to idle state. Looking at the next demo, it seems to be broken design.
Comment 12 Stanislav Brabec 2010-02-08 09:37:40 UTC
Created attachment 153253 [details]
motion-feedback-mainloop.c

Enhanced demonstration program that has option to delay some parts of processing (including the time consuming one) until the main loop enters idle.

It seems, that all possible combinations of updating position using gdk_window_get_pointer() work.

Well, I cannot explain why even gdk_window_get_pointer() in the motion callback and rendering in the idle loop work without causing delays.

Replacing gdk_window_get_pointer() by a function that works for multiple input devices can probably work in all cases.
Comment 13 Benjamin Moody 2010-02-09 02:53:31 UTC
I think we're pretty much in agreement, then.

A couple of fairly minor points:

1. What to do if there are multiple callbacks connected to the "motion-notify-event" signal on a widget?  Each such callback could query the pointer position itself, but that would (a) cause unnecessary server queries, and (b) cause the handlers to be unsynchronized.  I don't have a good answer to this.

2. Often, you only want to track pointer motion as long as, say, mouse button 1 is pressed (e.g., you want to "drag and drop" something.)  In most such cases (and especially in cases where motion hints make sense) we should also be tracking ButtonRelease events, and using the coordinates of the ButtonRelease for the "drop" coordinates, rather than the coordinates of the MotionNotify.  In some cases, though it might be necessary or helpful to check the "mask" returned by gdk_device_get_state(), and ignore the event if the button has already been released.
Comment 14 Stanislav Brabec 2010-02-09 09:52:35 UTC
1. It's an application responsibility. It is not a good idea to create more handlers in combination with motion event.

It has one important consequence:

Input filtering modules must never call gdk_window_get_pointer(), gdk_device_request_motions(), gdk_device_get_state(), gdk_device_get_history() (as they issue XQueryPointer() or XGetMotionEvents()).

All these functions must include this note in the documentation.

In particular, I am writing about libgtkstylus module, which does it and breaks motion hint processing (I already did a rewrite, but I am not yet satisfied with its results). Original: http://gpe.linuxtogo.org/download/source/ My rewrite: ftp://ftp.penguin.cz/pub/users/utx/gtk-modules/

2. I guess that GDK_BUTTON1_MOTION_MASK should work together with motion hints. And yes, this bug pattern is already in the "Blocks" tracker: bug 573154 issue 3.


Well, the summary of the proposal:

1. Deprecate gdk_event_request_motions()

2. Implement simple and technically correct function for updating coordinates in event handler (e. g. gdk_window_get_device_coordinates() as you propose in comment 6, but maybe it should be named gdk_device_get_...something... as other similar functions are named).

I could even imagine function specific for motion hint processing, e. g. gdk_event_update_coords(GdkEvent *event) (and/or gdk_event_get_current_coords() gdk_event_get_current_root_coords() counterparts of gdk_event_get_coords() resp. gdk_event_get_root_coords()), that will internally call gdk_window_get_device_coordinates() with correct arguments (GdkDevice *device, GdkWindow *window,...)

3. Document all consequences (functions not allowed in modules may be even undefined if programmer sets GTK_MODULE).

4. Find and fix all related bugs inside GTK+ itself: bug 573154 tracker

5. Check the non-X implementations.
Comment 15 Benjamin Moody 2010-02-13 21:30:00 UTC
Created attachment 153733 [details] [review]
Patch to implement gdk_device_get_position()

First patch: add a function "gdk_device_get_position()", which is essentially the multi-device version of gdk_window_get_pointer().
Comment 16 Benjamin Moody 2010-02-13 21:33:14 UTC
Created attachment 153734 [details] [review]
Patch to deprecate gdk_event_request_motions()

Second patch: deprecate gdk_event_request_motions() in favor of gdk_device_get_position().

For programs that need to remain compatible with older GDK versions, you can use the method given in comment #6.
Comment 17 Carlos Garnacho 2010-02-15 14:24:47 UTC
I'd vote to wait for deprecating gdk_event_request_motions() until the XI2 branch is merged, it also has API to know the device position relative to a window, gdk_window_get_device_position(), similar in semantics to gdk_window_get_pointer().
Comment 18 Owen Taylor 2010-02-15 16:44:37 UTC
Sorry for not reading and working through this entire bug, but the real solution looks more like:

 http://blog.fishsoup.net/2009/05/28/frames-not-idles/

I don't think there's any proper way of handling input that doesn't take output into account as well.
Comment 19 Benjamin Moody 2010-02-15 21:54:30 UTC
> I'd vote to wait for deprecating gdk_event_request_motions() until the XI2
> branch is merged, it also has API to know the device position relative to a
> window, gdk_window_get_device_position(), similar in semantics to
> gdk_window_get_pointer().

Would this be different in any way from the gdk_device_get_position() function I've suggested?  I'll change the name if you like; that way we can start fixing widgets *now*, and switch over to XI2 whenever that branch is ready.

> Sorry for not reading and working through this entire bug, but the real
> solution looks more like:
>
> http://blog.fishsoup.net/2009/05/28/frames-not-idles/
>
> I don't think there's any proper way of handling input that doesn't take
> output into account as well.

Interesting idea, and this might be a good solution in the long term.  As I understand it, you're suggesting that we wouldn't use PointerMotionHintMask at all, but instead, we would listen to all motion events, but for widgets that request "hint-like" behavior, we'd ignore all but the last in a series of motion events (similar to what's currently done for expose events.)  (I don't see how this is particularly tied to output, but OK.)

Would this be done at the GDK or GTK+ level?  And how would you indicate that a widget wants this "hint-like" behavior - using the existing GDK_POINTER_MOTION_HINT_MASK, or a completely new flag?  The former would be something of an API break (though, arguably, one that's consistent with a lot of existing code) and would mean that code written and tested using the new GTK+ would mysteriously fail when run using an older version.  The latter would mean that older programs couldn't take advantage of the new behavior, and GTK+/GDK would still have to support the traditional behavior of motion hints.
Comment 20 Owen Taylor 2010-02-15 23:15:31 UTC
(In reply to comment #19)

> > Sorry for not reading and working through this entire bug, but the real
> > solution looks more like:
> >
> > http://blog.fishsoup.net/2009/05/28/frames-not-idles/
> >
> > I don't think there's any proper way of handling input that doesn't take
> > output into account as well.
> 
> Interesting idea, and this might be a good solution in the long term.  As I
> understand it, you're suggesting that we wouldn't use PointerMotionHintMask at
> all, but instead, we would listen to all motion events, but for widgets that
> request "hint-like" behavior, we'd ignore all but the last in a series of
> motion events (similar to what's currently done for expose events.) 

Yes, basically. I'm not sure this is something that you request, or something that just happens by default. There is only a tiny, tiny set of widgets where you want something different - basically anything that is actually recording positions - a painting program or a handwriting recognizer.

And for those types of apps, an API to get history is probably fine.

> (I don't see how this is particularly tied to output, but OK.)

It's tied to output, because it's the only way you can define "last in a series of motion events" in a meaningful way. We want to update the UI exactly once for each frame we paint. It's pointless to do more, there's no reason to do less. If you keep on sending the application motion events as you receive more from the X server and never paint (get a series, send one to the app, get some more, send another, etc.), then the app can easily spend all its time updating the state and it does the user no good.

The flow control of PointerMotionHint is basically wrong because it occurs at the wrong time - when the app receives the event. And in fact, historically it was added to deal with 10mbps (or less) networks that could be killed by a flood of motion events. It had very little to do with making your app update smoothly without lag.

> Would this be done at the GDK or GTK+ level?  And how would you indicate that a
> widget wants this "hint-like" behavior - using the existing
> GDK_POINTER_MOTION_HINT_MASK, or a completely new flag?  The former would be
> something of an API break (though, arguably, one that's consistent with a lot
> of existing code) and would mean that code written and tested using the new
> GTK+ would mysteriously fail when run using an older version.  The latter would
> mean that older programs couldn't take advantage of the new behavior, and
> GTK+/GDK would still have to support the traditional behavior of motion hints.

I'd say it's something that spans between GTK+ and GDK because the current idle based resize and repaint logic is part in GTK+ and part in GDK. Without really working it through, I'd think it would likely be driven out of GDK. GTK+ would hook in for resizing by some sort of API where it could add "pre-frame work".

This would likely be a GTK+ 3 change (I don't think necessarily, but it avoids uncomfortable questions about "how compatible is compatible enough", and GTK+ 3 is approaching sooner than anybody thinks.) Basically moving to frame-based motion event handling makes all sorts of things that app and widget authors have struggled with just go away without any thinking. Grep for gdk_window_process_updates() in GTK+ to find some of the places where problems have been hacked around.
Comment 21 Matthias Clasen 2018-02-10 03:38:36 UTC
We're moving to gitlab! As part of this move, we are closing bugs that haven't seen activity in more than 5 years. If this issue is still imporant to you and
still relevant with GTK+ 3.22 or master, please consider creating a gitlab issue
for it.