GNOME Bugzilla – Bug 32617
gdk_pointer_grab() Behaves Differently for X Input Events
Last modified: 2011-02-04 16:11:52 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.
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)
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?
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.
Moving non-critical and hard-to-fix bugs to 2.0.2
Move open bugs from milestones 2.0.[012] -- > 2.0.3, since 2.0.2 is already out.
The gxi support has been removed for 2.2, so this may be easier to fix in that code base.
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)