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 32617 - gdk_pointer_grab() Behaves Differently for X Input Events
gdk_pointer_grab() Behaves Differently for X Input Events
Status: RESOLVED FIXED
Product: gtk+
Classification: Platform
Component: Backend: X11
1.2.x
Other All
: Normal normal
: ---
Assigned To: gtk-bugs
gtk-bugs
Depends on:
Blocks: 10498
 
 
Reported: 2000-11-19 16:25 UTC by gosgood
Modified: 2011-02-04 16:11 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
(8.03 KB, text/plain)
2001-01-27 19:48 UTC, Exported from Debbugs
Details
(885 bytes, text/plain)
2001-01-27 19:48 UTC, Exported from Debbugs
Details

Description gosgood 2001-01-27 19:48:48 UTC
Package: gtk+
Version: 1.2.8

Name........: Garry R. Osgood
Email.......: gosgood@idt.net
Platform....: SGI Indigo IP20, SGI O2 IP32-5K Red Hat Linux 6.2 (Zoot) on Dell PIII (2.2.14-6.1.1)
GTK Version.: 1.2.8

-- Problem description

This bug draws attention to gdk_pointer_grab() which does not identically
handle X11 Core and XInput Extension events. The function behaves differently
depending on whether the source of events are GDK_SOURCE_MOUSE or GDK_SOURCE_PEN,
violating the documented calling semantics of the function interface which 
makes no reference to differences in behaviour between the two sources. See
http://developer.gnome.org/doc/API/gdk/gdk-general.html#GDK-POINTER-GRAB .

In particular, when gdk_pointer_grab() is assembling XGrabPointer (3X11)
event masks it confines itself to simply remapping GDK event masks into 
X11 equivalents, otherwise passing the request unchanged to XGrabPointer().

But for XGrabDevice(), the function that builds the analogous event list,
gdk_input_common_find_events(), embarks on a policy of including GDK_BUTTON_PRESS_MASK
if a release as been requested, and GDK_BUTTON_RELEASE_MASK if a press has
been requested. Insofar as this function is concerned, both are requested
if any one is requested on behalf of the caller. The source of this policy
may be found in a nearby comment that suggests (in part) "we have to track press 
and release events in pairs to keep track of button state correctly..."
[gdkinputcommon.h Line 325 ff]

Perhaps, but isn't that up to the caller of gdk_pointer_grab() to decide? 
Such a caller gets exactly the type of events anticipated with core events, but
with extension events she acquires misguidedly furnished button press events as
well. She didn't ask for them, doesn't expect their arrival, and, if she's working
with Gimp 1.1.xx code, suddenly finds her application responding to button
press events she honestly felt was filtered from the stream. This circumstance 
in fact is the basis of the current Gimp 1.1.xx bug #10498, where tablet button
presses are found to be unexpectedly triggering context menus, which often cause the unloading
of tools before their time (killing the marching ants, a visual side effect of
a disrupted tool state).

gdk_input_common_find_events() should confine itself to the specific task of mapping
the given gdk event mask into a selection of XInput classes. Period. While its concern for
anticipated application or widget system needs is kindly motivated, it in fact gives
rise to different behaviour in gdk_pointer_grab() depending on whether the pointer
is originating from the X11 core (source type is GDK_SOURCE_MOUSE) or X Input 
(GDK_SOURCE_PEN). Nothing in gdk_pointer_grab() documentation warns of this asymmetry,
so the kindly motivated behaviour is giving rise to asymmetric grab semantics.

-- Detail

When an application such as Gimp desires to grab the current pointer to manage (as
an example) the drawing of a selection rectangle, it may issue:

  return_val = gdk_pointer_grab (
                                  <window>,                      /* Assert on this window */
                                  FALSE,                         /* owner_events */
                                  GDK_POINTER_MOTION_HINT_MASK | /* only report these events */
                                  GDK_BUTTON1_MOTION_MASK |      
                                  GDK_BUTTON_RELEASE_MASK,
                                  NULL,                          /* No window confine needed */
                                  NULL,                          /* Normal cursor requested  */       
                                  bevent->time
                                );

The OR'ing of masks limits the kinds of events which are to be conveyed to the
grabbed window. In this example from Gimp code, a selection is in progress: the
application is interested in motion events to keep the selection geometry updated;
it is interested in release events to signal the completion of selection, it is NOT
interested in button presses because (1) presses are used to signal start of different
selections (2) the Gimp event dispatch code can use the event to switch to another
tool context. The second possibility gives rise to significant tool instability.

When the core input device is selected and the GDKEvent instances are stamped with
GDK_SOURCE_MOUSE, this function behaves as expected. the implementation of this function
simply remaps GDK event masks into X11 equivalents and calls XGrabPointer. 

When an X11 Input Extension device is selected and the GDKEvent instances are stampled
with GDK_SOURCE_PEN, this function calls, for each X11 input extension device that has been
opened, gdk_input_common_find_events() to access the GdkDevicePrivate structures 
associated with each device and determine the equivalent event classes to implement
the caller-supplied GDK_XXX_MASK. If it confined its scope of activity to this, there
would be no difficulty, but it goes on to implementing a policy of linking together 
GDK_BUTTON_PRESS_MASK and GDK_BUTTON_RELEASE_MASK, so that if one is requested the
other is automatically added to the XGrabDevice request as well. This is clearly
asymmetric with respect to core pointer events. As a nearby comment suggests, this may
be commonly desirable among applications, but it is not in this function's purview
to summarily decide this, and the asymmetry of behaviour it gives rise to with
gdk_pointer_grab() is not desirable. 

-- How to repeat

Set a breakpoint at gdk_input_common_find_events() and compare the event classes 
passed to XGrabDevice with the intent conveyed in the GDK event mask.

-- Other system notes

I plan to upload a suggested patch to ftp.gtk.org probably withing two to three 
days of this mail. 

Be good, be well

Garry Osgood




------- Additional Comments From otaylor@redhat.com 2000-11-20 18:32:49 ----

Subject: Re: Bug#32617: gdk_pointer_grab() Behaves Differently for X Input Events
From: Owen Taylor <otaylor@redhat.com>
To: 32617@bugs.gnome.org
Cc: "Garry R. Osgood" <gosgood@idt.net>, 
Message-Id: <yben1eudxsu.fsf@fresnel.labs.redhat.com>
Date: 20 Nov 2000 18:32:49 -0500


I haven't had the opportunity to go through this in detail yet, but I
would suggest that having the GIMP depend on not receiving events is
poor design practice, and not necessary.

Although if I have an excess of time I might try to code a workaround
to hide the fact that GTK+ has to select for both press and release
internally, I would suggest you simply add the 1-2 lines of code to
the GIMP that are necessary to work around the problem.

Regards,
                                        Owen




------- Bug moved to this database by debbugs-export@bugzilla.gnome.org 2001-01-27 14:48 -------
This bug was previously known as bug 32617 at http://bugs.gnome.org/
http://bugs.gnome.org/show_bug.cgi?id=32617
Originally filed under the gtk+ product and general component.

The original reporter (gosgood@idt.net) of this bug does not have an account here.
Reassigning to the exporter, debbugs-export@bugzilla.gnome.org.
Reassigning to the default owner of the component, gtk-bugs@gtk.org.

Comment 1 Owen Taylor 2001-02-03 00:50:06 UTC
A followup that didn't get attached to the bug:

From: "Garry R. Osgood" <gosgood@idt.net>
Subject: Bug#32617: [Fwd: Re: Bug#32617: gdk_pointer_grab() Behaves
Differently for X Input   Events]
To: 32617@bugs.gnome.org
Date: Tue, 21 Nov 2000 20:55:24 -0500
Reply-To: "Garry R. Osgood" <gosgood@idt.net>, 32617@bugs.gnome.org
Resent-From: "Garry R. Osgood" <gosgood@idt.net>


Hi Owen,

Thank you for your quick reply. I appreciate that.

Owen Taylor wrote:

> I haven't had the opportunity to go through this in detail yet, but
I
> would suggest that having the GIMP depend on not receiving events is
> poor design practice, and not necessary.
>
> Although if I have an excess of time I might try to code a
workaround
> to hide the fact that GTK+ has to select for both press and release
> internally, I would suggest you simply add the 1-2 lines of code to
> the GIMP that are necessary to work around the problem.
>
> Regards,
>                                         Owen

The issue is the uniformity of behaviour of a calling interface.
Applications may use an interface wisely or stupidly; in both cases
the interface still must function as a contract between the interface
designer and user, however unskilled the latter may be. In fact, by
hiding complexity and offering a simple-to-use interface, a less-than-
highly skilled interface user can still function at some level of
effectiveness. Not so if an interface is large, inconsistent, and
full of Strange Rules that only highly-paid experts can understand.

How does an interface approach ease-of-use? Offering uniformity
of behaviour on the user side -- even as the interface implementation
maps to diverse low level services --has got to be near the top of
the list. That's why CORBA is so popular here in the financial
industry. Oracle v. Sybase? Who cares. Write a CORBA service to
normalize the peculiarities of each. 

One component of gdk_pointer_grab() interface is the event mask. 
It's there so applications can ask X servers to limit
event traffic while they perform temporary trickiness like
draw rubber rectangles. Such a mask should include events that
prompts the application to break the grab; it should also exclude
events that might otherwise trigger diversionary behaviour. 
Which is which is up to the app designer to figure out. GTK+
wisely extrudes the choice up from XGrabPointer so that the
app designer can make her choices, good or ill.

GTK also does something else which is very good interface
design indeed. It keeps under the hood the two X-level grab management
tools,
XGrabPointer() and XGrabDevice(), the latter a Beast to correctly use, 
and presenting instead a one-stop-shopping call, gdk_pointer_grab(), 
that manages whatever the H-E-Double-Toothpicks is doing the pointing. 
Core pointer? X Input Extention tablet? -- who cares. Don't worry. 
Use gdk_pointer_grab(). It'll do the Right Thing.

And it does. Almost.

Here's the rub: When the end user of the application has disabled the
XInput extension device(s) and is only using the core pointer, a
GDK style event mask passed to gdk_pointer_grab() is simply mapped
to the X equivalents. It's a very simple loop that lives from lines
660 - 665 of gdk.c, in the implementation of gdk_pointer_grab().
and it translates a set of GDK event flags:

GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON1_MOTION_MASK |
GDK_BUTTON_RELEASE_MASK,

into their X equivalents without a change in meaning. 

Now the end user says, "Hot d**n!!! The Input Extension is disabled!!!
No wonder the Ink tool is so lifeless and tame!!! I need some
pressure;
I need some X-Y tilt action!!!" and enables the XInput Extension
device.

Of course, the mapping of a GDK style event mask to XInput events
cannot be handled
by a simple loop. Unlike their core counterparts, there are no
manifest 
constants representing XInput events. As the X server becomes aware of
the kinds of events extension devices can produce, it assigns numbers,
event classes, dynamically. To map against GDK style event masks, 
GTK-based X clients first have to cache the event
classes returned in the XDevice (XDevice::XInputClassInfo array, a
part
of the object returned by XOpenDevice) and then understand how these 
numbers fall into the various event categories (key, motion, input,
state, 
mapping, change). The XInput extention headers define macros
(DeviceButtonPress, DeviceButtonRelease...) to do much of the event
categorization heavy
lifting, but still, what can be done with a simple loop for core
event masks [lines 660-665, gdk.c] requires
gdk_input_common_get_events()
[gdkinputcommon.h lines 308 ff] to assemble an event class list.
Ultimately, 
XGrabDevice(), the XInput event counterpart of XGrabPointer(), 
consumes this list [gdkinputxfree.h line 310].

The motivation for #32617 arises when one compares the functional 
behaviour of lines [660-665] of gdk.c with
gdk_input_common_get_events().
Both take as input a OR'ed GDK_<event_category_identifier>_MASK and
produce
X equivalents: a new mask for XGrabPointer() to select a set of core
events,
or a new array of event classes for XGrabDevice() to select a set of
extension events.

But lines [660-665] of gdk.c simply remaps one mask to another. They
do not, 
in effect, say "the application writer does not know how to ask for
the right
event categories. I'll add this category and take that away." In
contrast, gdk_input_common_get_events() does just that with a linkage
between
GDK_BUTTON_PRESS_MASK and GDK_BUTTON_RELEASE_MASK. the function
summarily decides
that the user must be bit thick asking for release events without
press events 
(or vice-versa). The merits of that judgement aside, the choice
changes the
behaviour of gdk_pointer_grab() for XInput extension events, because: 

GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON1_MOTION_MASK |
GDK_BUTTON_RELEASE_MASK

suddenly becomes (in effect):

GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON1_MOTION_MASK |
GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK

This is an unexpected and unanticipated change for the interface user,
who has
been given the impression from the documentation
(http://developer.gnome.org/doc/API/gdk/gdk-general.html#GDK-POINTER-GRAB) 
that gdk_pointer_grab() grabs the pointer, regardless of its physical 
implementation. An aside is made that the pointer is (usually) the
core mouse pointer,
but that aside does not further spell out how differences arise when
the pointer 
is based on an XInput extension tablet interface, or what those
differences are.

If we take as principle that an interface exhibit constant behaviour
(exceptions
and boundary conditions well documented) then it seems to me there are
two 
courses of action:

1) make lines 660-665 of gdk.c functionally equivalent to
gdk_input_common_find_events()
   [have internal override logic to edit users' raw
GDK_<event_category_identifier>_MASK]

2) make gdk_input_common_get_events()functionally equivalent to lines
660-665 of gdk.c  
   [just transcribe the event category identifiers: the user had
better know how to ask 
    for the right thing].

I would personally find 1) the more difficult course, for it puts the
onus of being "smart"
about event lists in the GTK side of the interface, and I would be
constantly interpreting
what the interface user "should" be asking for. Lines 660 - 665 will
grow into something
more complicated as it undertakes to be as "smart" about X core event
masks as gdk_input_common_get_events() is about X Input event
classes.  

2) is the cleaner approach. It places legitimate responsibility on the
user to know what
event categories are and how to use them correctly. GTK normalizes
core events and X input events
and extrudes them to the user through one call only,
gdk_pointer_grab(), (a Good Interface practice) but does not otherwise
modify the intent of the event_mask parameter (also a Good Interface
practice: if the user asks for silliness, it's the user's bug to fix,
not GTK's).

Attached is a patch to gdk_input_common_get_events() that illustrates
course 2). I recognize that
that there is a input=gxid case which this patch does not address; I
am currently testing
this patch for input=xfree on both SGI's and Red Hat Linux. It
addresses bug #10498 in the
Gimp world -- the testing effort is currently looking for gotchas and
ass-biters.

Thank you for your prompt reply, and thank you for attending me this
far. I trust we are all
in the business of making GTK (and Open Source) more viable solutions
for businesses and 
weekend warriors; we just may have different ideas about approach from
time to time.

Be good, be well

Garry--- /alice/WorkSpace/gtk+-1.2.8/gdk/gdkinputcommon.h-orig	Sat Nov
27 02:51:24 1999
+++ /alice/WorkSpace/gdkinputcommon.h	Sun Nov 19 14:09:36 2000
@@ -315,18 +315,17 @@
   XEventClass class;
   
   i = 0;
-  /* We have to track press and release events in pairs to keep
-     track of button state correctly and implement grabbing for
-     the gxi support */
-  if (mask & GDK_BUTTON_PRESS_MASK || mask & GDK_BUTTON_RELEASE_MASK)
+
+  if (mask & GDK_BUTTON_PRESS_MASK)
     {
       DeviceButtonPress (gdkdev->xdevice, gdkdev->buttonpress_type,
 			     class);
       if (class != 0)
 	  classes[i++] = class;
-      DeviceButtonPressGrab (gdkdev->xdevice, 0, class);
-      if (class != 0)
-	  classes[i++] = class;
+    }
+
+  if (mask & GDK_BUTTON_RELEASE_MASK)
+    {
       DeviceButtonRelease (gdkdev->xdevice,
gdkdev->buttonrelease_type,
 			   class);
       if (class != 0)

Comment 2 Daniel Egger 2001-02-06 19:03:27 UTC
Owen? I guess you have the word on this one. This bug blocks
bug #10498 and I'd like to have at least a consensus what to
do on this issue. Which two lines were you talking about, BTW?
Comment 3 Owen Taylor 2001-02-27 08:40:03 UTC
I'm removing this from the 1.2.9 milestone, because fixing
this and testing the fix is simply more work than I have
time to do right now.

(As mentioned earlier, the above patch doesn't come close
to being a correct fix, because I wasn't just adding release
into the event mask for the fun of it - the paired 
press/release events are used elsewhere in the code.)

The "2-line fix" (two lines wasn't meant to be taken literally),
is, when you grab the mouse, to set a flag saying "I've grabbed
the mouse, and, when that flag is set, to ignore button presses.
Comment 4 Owen Taylor 2002-03-25 23:30:55 UTC
Moving non-critical and hard-to-fix bugs to 2.0.2
Comment 5 Matthias Clasen 2002-04-05 13:33:05 UTC
Move open bugs from milestones 2.0.[012] -- > 2.0.3, since 2.0.2 is already out.
Comment 6 Owen Taylor 2002-05-01 22:53:37 UTC
The gxi support has been removed for 2.2, so this may be
easier to fix in that code base.
Comment 7 Owen Taylor 2002-12-09 23:56:59 UTC
Mon Dec  9 18:50:31 2002  Owen Taylor  <otaylor@redhat.com>

        * gdk/x11/gdkinput-x11.c (_gdk_input_common_find_events):
        Remove selection for DeviceButtonRelease, we no longer need
        it now that gxi is gone. (#32617, reported by Garry
        Osgood)