GDK and XInput [Was: Re: Thanks for Tablet Testing]
Simon, Raphael, All... I had the opportunity to spend three or four hours yesterday afternoon/evening (GMT -5.00) seeking ways to rescue Marching Ants (#10498). I can confidently report that a call such as: return_val = gdk_pointer_grab (gdisp-canvas-window, FALSE, GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON1_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, NULL, NULL, bevent-time); does not behave identically for core events (Defined in the original X11 protocol) and XInput Extension events. This is true when GTK+-1.2.8 has been built with the --xinput=xfree option. While the GDK event request list is honored for core X11 protocol events, the XInput events are subject to special handling. The culprit, gdk_input_common_find_events(), while nominally in charge of just mapping the the GDK event request list into appropriate XInput device classes [gtk+-1.2.8/gdk/gdkinputcommon.h], it in fact asserts an internal policy of linking symmetric events. That is, this code virtually adds GDK_BUTTON_PRESS_MASK when a client requests only GDK_BUTTON_RELEASE_MASK -- and vice-versa. The comment is unhelpful: /* 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) { DeviceButtonPress (gdkdev-xdevice, gdkdev-buttonpress_type, class); if (class != 0) classes[i++] = class; ... [gdk_input_common_find_event() gdkinputcommon.h Line 322 ff] It is not clear to me why "button state" matters to GDK or why GTK+-1.2.8/GDK requires events to be linked in this fashion -- for XInput Extension devices only. To the contrary, Gimp' family of selection tools require the alternate circumstance: that during the grab, button press events cannot be permitted to arrive, else the grand Gimp event dispatcher, gdisplay_canvas_events() [disp_callbacks.c], would divert Gimp process flow into a (likely) menu dispatch. Since many menu implementations are "tool-like", this can lead to the unloading of the current selection tool before it has had opportunity to put its persistent state in order -- thaw the undo stack, for example. This is especially true for the "Trojan Horse" tool, edit-select, which, through invitation by some other select tool (invoking init_edit_selection()), temporarily substitutes its predecessor's tool methods for its own. Should this tool become unloaded because of a menu-related button press, the predecessor's finalize methods are not called and the tool itself does not have full opportunity to clean up. What to do? Certainly, bug reports to GTK are in order: GTK "promises" to abstract the pointer so that the application writer does not have to do anything special with core and extension type events. Grab semantics should be uniform. Now that I am confident of the chain of causuality, I can handle that. In light of an (is it coming? Really?) 1.2 Release The question I have for the group is: 1) Document, warn, but otherwise ignore the problem. It affects users with a certain type of tablet hardware and only when that hardware is being used as an explicit XInput device. Wait for a GDK fix to remove its hidden policy? 2) Make a Gimp level hack in the much-abused event loop to filter button presses that originate from devices when a grab is in effect. (not pretty -- except for possibly being pretty lame)? 3) Re-engineer select tool code to be more robust in button press events (much work here)? Which of these is the best line of action? Do you have other proposals? If no one objects, I would like to elevate #10498 from 'critical' to 'grave.' Through a chain of causuality originating with edit-select not being able to perform a thaw, eventually (sometimes) there is a fatal crash in undo. Not sure why, but grave bugs are the ones that crash Gimp - at least sometimes. Thank you to Simon and Raphael for thoughts, observations, snippets of test code. As an aside, I think Simon is correct in observing that this bug is also related to Bug #6901, "Can not continually move a floating selection with a pressure sensitive pointer."? Be good, be well Garry
Re: Thanks for tablet testing...
Simon, Raphael Simon: Thank you for the Havoc Pennington/X-11 Consortium documentation As Raphael points out, the GDK/XInputExtension glacis is where interesting affairs transpire. Raphael wrote: I think that #10498 occurs because of a combination of two things: - A bug in GTK+ (in the interaction between XInput and the core pointer) breaks the semantics of the pointer grab, so the application (Gimp) receives some events that should never occur while the pointer is grabbed. I suspect this. I'm not at the stage where I actually believe this. - For the ArtPad II under XFree86, another bug in XFree shadows the GTK+ bug and causes other strange things to happen. Can't say anything useful here, since I am testing without XFree86 (Setup uses Xsgi) If this theory is correct, then the Gimp 1.2 should require a new release of GTK+. :-( I am rather leaning toward this sad conclusion. However, at Gimp level, we never check gdk_pointer_grab() to confirm we have the pointer, and that is suspect [example: app/rect_select.c line 173] Pennington tells us we should (See 10.6.2 GTK/Gnome App Dev, first reference in Simon's mail] Also, we limit grab to the current window only, not to child windows (like menu panes?) so we can lose control of focus, no? When the pen point (simulated left mouse button) is already down and a grab has been made from rect_select, then here's what watch points at gdk_pointer grab report: (Tablet device enabled and active) (Rect Selection already made) (Cursor in selection) (pen point goes down...) only asking for grab on current window V 0 gdk_pointer_grab(window = 0x107bcb50, owner_events = 0, event_mask = 552, confine_to = 0x0, cursor = 0x0, time = 42275620) ["gdk.c":694, 0x0400cdec] 1 rect_select_button_press(tool = 0x10777d10, bevent = 0x103e79c8, gdisp_ptr = 0x107bb6c8) ["rect_select.c":173, 0x101e3244] 2 gdisplay_canvas_events(canvas = 0x107b6320, event = 0x103e79c8) ["disp_callbacks.c":281, 0x100b0878] rest of stack snipped... gdk_xgrab_window = 0x107bcb50 -- gimp image display window return_val = 0-- grab was a success (Floating selection being dragged, then "right mouse button" simulated depressed on pen body) Menu pane asks for self child V 0 gdk_pointer_grab(window = 0x1087a308, owner_events = 1, event_mask = 13060, confine_to = 0x0, cursor = 0x108bb570, time = 42288140) ["gdk.c":694, 0x0400cdec] 1 gtk_menu_popup(menu = 0x1065ed70, parent_menu_shell = 0x0, parent_menu_item = 0x0, func = 0x0, data = 0x0, button = 3, activate_time = 42288140) ["gtkmenu.c":493, 0x5fed58bc] 2 gdisplay_canvas_events(canvas = 0x107b6320, event = 0x103e79c8) rest of stack snipped... gdk_xgrab_window = 0x1087a308 -- menu pane window (child of gimp image window, yes ???) return_val = 0-- menu pane gets its pointer grab... The hunt continues Garry