
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