Hi, I've been exploring how widgets with no GdkWindow could receive events. Here are some notes so far in case people have thoughts.
Event Types === Events separate very cleanly into "weird lowlevel stuff only matters for GdkWindow" and "things widgets in general including no-window widgets care about" A. Events all widgets care about have two subdivisions: A1. events that bubble: GDK_MOTION_NOTIFY, GDK_BUTTON_PRESS, GDK_2BUTTON_PRESS, GDK_3BUTTON_PRESS, GDK_BUTTON_RELEASE, GDK_SCROLL, GDK_KEY_PRESS, GDK_KEY_RELEASE A2. events that are glorified property notifies: GDK_ENTER_NOTIFY, GDK_LEAVE_NOTIFY, GDK_FOCUS_CHANGE B. Freaky events of interest only in context of GdkWindow or GdkDisplay: GDK_DELETE, GDK_DESTROY, GDK_EXPOSE, GDK_CONFIGURE, GDK_MAP, GDK_UNMAP, GDK_PROPERTY_NOTIFY, GDK_SELECTION_CLEAR, GDK_SELECTION_REQUEST, GDK_SELECTION_NOTIFY, GDK_PROXIMITY_IN, GDK_PROXIMITY_OUT, GDK_DRAG_ENTER, GDK_DRAG_LEAVE, GDK_DRAG_MOTION, GDK_DRAG_STATUS, GDK_DROP_START, GDK_DROP_FINISHED, GDK_CLIENT_EVENT, GDK_VISIBILITY_NOTIFY, GDK_NO_EXPOSE, GDK_WINDOW_STATE, GDK_SETTING, GDK_OWNER_CHANGE, GDK_GRAB_BROKEN, GDK_DAMAGE Unfortunately, the current ::event signal (and per-event-type sub-signals) dump all this together. These are logically very distinct and ideally if we were starting from scratch, I would probably want to see: A1. a "GtkEvent" with some of the obscure lowlevel fields removed, no ->window field, add ->widget field, coords are widget-relative A2. just use property notifies on "contains-pointer" and "has-focus" properties B. GdkEvent as it is now (well, cleaned up, but similar idea) Splitting out B (Window Owners) === A challenge is to think about all code related to B as conceptually a new type, GtkWindowOwner or something. The only instances of this type would be GtkWindow, GtkPlug, GtkSocket, and maybe a couple other "weird" widgets like GL area. Unfortunately this type is sort of a mixin; if it were an abstract base class, then widgets could not be both a GtkWindowOwner and a GtkContainer. This probably means that the WindowOwner functionality has to remain in GtkWidget. Stuff that belongs to the conceptual window owner type could include: the signals for all the B events; event mask; set_window; set_double_buffered; GdkVisual; style_attach(); shape mask. I don't think this stuff can easily be actually split out of GtkWidget. However, I do think it's helpful to think of it as distinct. _Possibly_ it would be clearer to add an interface that explicitly had all this, and make GtkWidget implement that interface, and deprecate the old gtk_widget_ names someday. Or maybe there's a nice solution involving delegation, where window widgets have a helper object and there's a base class for that. Enter/Leave Notify === Here is how I think enter/leave should work: http://bugzilla.clutter-project.org/show_bug.cgi?id=1576 Though I'm not sure events are required at all. Currently, something like GtkButton needs the input-only window just to prelight. I think a simple boolean property "button or any child of button contains pointer" would be sufficient for this, however. Multiple GdkDevice pointers complicates that a bit, though it doesn't look like GtkButton handles that right now. The boolean could be defined to mean that greater-than-zero devices are inside the widget and that would probably work fine for prelighting, better than the current enter/leave events in fact. My current thought here is to have a boolean prop which is set on the leaf widget containing each device and all ancestors of that widget. For convenience, maybe a vfunc or vfunc pair which is invoked whenever this property changes. notify::contains-pointer would already be a signal for it but that's sort of annoying in subclasses. I guess this flag would simply mean "prelight" and that might be handy for the new theme stuff. Given the property, the event would be only on conceptual GtkWindowOwner. No-window widgets would use the property not the event. Implementing "contains-pointer" for no-window widgets would require some kind of pick() virtual method for identifying the widget under the pointer, GtkWidget default implementation just looks at widget allocations, GtkContainer default recurses into its children. Picking would start at the window widget receiving a GdkWindow native motion notify or enter/leave and then generate synthetic events for no-window widgets inside that GdkWindow. Focus Change === I believe the existing has-focus pretty much handles this; similar to the above-proposed notify::contains-pointer, it might be handy to have a virtual function to go with it, or virtual function pair. The fields in the focus change event are only of interest for GtkWindowOwner not for widgets in general, it looks like. Given the property, the event would be only on conceptual GtkWindowOwner. No-window widgets would use the property not the event. Bubble Events === If we solve "A2" enter/leave/focus events with properties, "A1" remains. I've played around with code on A1 and here are some of the issues. 1. GdkEvent's window-relative coordinates don't mean anything on a no-window widget. My initial experiment is to add new API gdk_event_get_window_coords(). gdk_event_get_coords() always returns window coords when the event comes out of GDK. However, some (or all) APIs in GTK would have the ability to gdk_event_set_coords(), at which point the "coords" and the "window coords" would not be the same. The idea would be to allow widget-relative events. 2. GdkEvent's "window" field doesn't mean anything on a no-window widget or as the event bubbles. My initial experiment is to add gdk_event_set_source(event, GObject) which GTK uses to store the event widget on the event. 3. Include no-window widgets in the "pick the widget to bubble from" code in gtkmain.c, which is the grab logic plus mapping windows to widgets. My initial experiment is to add a GtkEventReceiver interface, implemented by Widget, which has a pick_source() method. pick_source() takes a GdkEvent and comes up with the source widget. pick_source() is almost the same as the pick() used to track contains-pointer, except that it handles key events also. I haven't really sorted out grabs yet but basically I think it's a vfunc used to recursively punt up to the toplevel which would then implement the actual logic. 4. Replace gtk_propagate_event() with something that translates the event to widget-relative coords at each step. My initial experiment is a GtkEventReceiver interface with capture_event and bubble_event vfuncs. These are recursively called to first capture then bubble the event. GtkWindow uses its capture_event to do the special-cased key event handling that's in gtkmain.c now. As events are captured and bubbled, their coordinates are translated. In addition to enabling no-window widgets, this adds capture functionality to GTK and cleans up some of the "global omniscience" design of gtkmain.c in favor of recursive vfuncs. 5. Event masks. If you're a no-window widget, and you want button presses, then you need your containing window widget to have selected for button press event. Possible solutions include: - my preference so far: just always selecting for button, key, scroll, and motion events on any GdkWindow used for a gtk_widget_set_window() - tracking a combined event mask of all children inside window widgets 6. Delivering events to each widget during capture and bubble. This issue, how to emit a widget-relative event on GtkWidget, is THE big central API question. During capture, a signal like ::event-captured seems logical. During bubble, a signal like ::event seems logical. Unfortunately that signal already exists but its GdkEvent is window-relative to the original event window, rather than widget-relative to the widget currently getting bubbled through. Possible solutions include: - add a new signal ::event-bubbled with the widget-relative events. - break ::event to be widget-relative, but in my initial experimentation this looked like it'd be pretty disruptive even within GTK - gtk_widget_class_set_widget_relative_events() toggles what kind of coords each subclass receives to ::event - leave event->x, event->y window-relative and add new GdkEvent fields for the widget-relative problem with ::event-bubbled is that it would be nice to have the button-press-event etc. individual signals too and ::button-press-event-bubbled is a mouthful of a name. plus it's confusing that the signals you really want are named weirdly "-bubbled" and the old ones you don't are named better. i.e. you'd be supposed to always use ::event-bubbled and ignore ::event, etc. This quickly leads to dreams of just making the existing signals widget-relative, but that change is very disruptive and the compiler can't detect it at all. gtk_widget_class_set_widget_relative_events() works pretty well for subclassing but makes a mess if apps connect to the event signals since they have to care how the widget set them up. Adding widget coords to GdkEvent would be nice, but the intractable problem here is that the widget coords really need to be event->x, event->y and the window coords event->x_window, y_window. Otherwise, 1) there's no name for the widget coords unless we use the concept of widget in GDK and 2) we'd have to explain in the docs and on mailing lists that event->x, event->y were red herrings, don't use them. Tough to do without embarrassment. Summary === The implementation is not hard. (Though some care is needed to keep the semantics of grabs and key events and such.) The main problem is that the existing window-relative signals and event->x,y use up the namespace you'd want to use for widget-relative signals. My preference so far would be to go with a new ::event-bubbled signal I think, plus (ouch) {button-press,button-release,key-press,key-release,scroll,motion}-bubbled. (or some better names...?) And have event->x, event->y be either window or widget relative depending on the signal the event is passed to. Basically there's an old and a new set of signals. They could be on two new broken-out interfaces for clarity, GtkWindowOwner and GtkEventReceiver. Assuming the disruptive API changes are avoided (they are all optional, just prettier), I think this is a very doable project. Far smaller in scope than the rendering cleanup, for example. This change would kill all the input-only windows. So the remaining windows would be the ones used for scroll and clip. Solving scroll and clip would then make GdkWindow fall away entirely except for toplevels and the plug/socket/GL stuff that genuinely cares about a native window. Havoc _______________________________________________ gtk-devel-list mailing list gtk-devel-list@gnome.org http://mail.gnome.org/mailman/listinfo/gtk-devel-list