static void clutter_bind_constraint_set_actor (ClutterActorMeta *meta, ClutterActor *new_actor) { ClutterBindConstraint *bind = CLUTTER_BIND_CONSTRAINT (meta); ClutterActorMetaClass *parent; if (new_actor != NULL && bind->source != NULL && clutter_actor_contains (new_actor, bind->source)) { g_warning (G_STRLOC ": The source actor '%s' is contained " "by the actor '%s' associated to the constraint " "'%s'", _clutter_actor_get_debug_name (bind->source), _clutter_actor_get_debug_name (new_actor), _clutter_actor_meta_get_debug_name (meta)); return; } /* store the pointer to the actor, for later use */ bind->actor = new_actor; parent = CLUTTER_ACTOR_META_CLASS (clutter_bind_constraint_parent_class); parent->set_actor (meta, new_actor); }
/** * clutter_bind_constraint_set_source: * @constraint: a #ClutterBindConstraint * @source: (allow-none): a #ClutterActor, or %NULL to unset the source * * Sets the source #ClutterActor for the constraint * * Since: 1.4 */ void clutter_bind_constraint_set_source (ClutterBindConstraint *constraint, ClutterActor *source) { ClutterActor *old_source, *actor; ClutterActorMeta *meta; g_return_if_fail (CLUTTER_IS_BIND_CONSTRAINT (constraint)); g_return_if_fail (source == NULL || CLUTTER_IS_ACTOR (source)); if (constraint->source == source) return; meta = CLUTTER_ACTOR_META (constraint); actor = clutter_actor_meta_get_actor (meta); if (source != NULL && actor != NULL) { if (clutter_actor_contains (actor, source)) { g_warning (G_STRLOC ": The source actor '%s' is contained " "by the actor '%s' associated to the constraint " "'%s'", _clutter_actor_get_debug_name (source), _clutter_actor_get_debug_name (actor), _clutter_actor_meta_get_debug_name (meta)); return; } } old_source = constraint->source; if (old_source != NULL) { g_signal_handlers_disconnect_by_func (old_source, G_CALLBACK (source_destroyed), constraint); g_signal_handlers_disconnect_by_func (old_source, G_CALLBACK (source_queue_relayout), constraint); } constraint->source = source; if (constraint->source != NULL) { g_signal_connect (constraint->source, "queue-relayout", G_CALLBACK (source_queue_relayout), constraint); g_signal_connect (constraint->source, "destroy", G_CALLBACK (source_destroyed), constraint); if (constraint->actor != NULL) clutter_actor_queue_relayout (constraint->actor); } g_object_notify_by_pspec (G_OBJECT (constraint), obj_props[PROP_SOURCE]); }
/** * clutter_align_constraint_set_source: * @align: a #ClutterAlignConstraint * @source: (allow-none): a #ClutterActor, or %NULL to unset the source * * Sets the source of the alignment constraint * * Since: 1.4 */ void clutter_align_constraint_set_source (ClutterAlignConstraint *align, ClutterActor *source) { ClutterActor *old_source, *actor; ClutterActorMeta *meta; g_return_if_fail (CLUTTER_IS_ALIGN_CONSTRAINT (align)); g_return_if_fail (source == NULL || CLUTTER_IS_ACTOR (source)); if (align->source == source) return; meta = CLUTTER_ACTOR_META (align); actor = clutter_actor_meta_get_actor (meta); if (actor != NULL && source != NULL) { if (clutter_actor_contains (actor, source)) { g_warning (G_STRLOC ": The source actor '%s' is contained " "by the actor '%s' associated to the constraint " "'%s'", _clutter_actor_get_debug_name (source), _clutter_actor_get_debug_name (actor), _clutter_actor_meta_get_debug_name (meta)); return; } } old_source = align->source; if (old_source != NULL) { g_signal_handlers_disconnect_by_func (old_source, G_CALLBACK (source_destroyed), align); g_signal_handlers_disconnect_by_func (old_source, G_CALLBACK (source_position_changed), align); } align->source = source; if (align->source != NULL) { g_signal_connect (align->source, "allocation-changed", G_CALLBACK (source_position_changed), align); g_signal_connect (align->source, "destroy", G_CALLBACK (source_destroyed), align); if (align->actor != NULL) clutter_actor_queue_relayout (align->actor); } g_object_notify_by_pspec (G_OBJECT (align), obj_props[PROP_SOURCE]); }
static inline void warn_vertical_edge (const gchar *edge, ClutterActor *actor, ClutterActor *source) { g_warning (G_STRLOC ": the %s edge of actor '%s' can only " "be snapped to the top or bottom edge of actor '%s'", edge, _clutter_actor_get_debug_name (actor), _clutter_actor_get_debug_name (source)); }
static inline void warn_horizontal_edge (const gchar *edge, ClutterActor *actor, ClutterActor *source) { g_warning (G_STRLOC ": the %s edge of actor '%s' can only be snapped " "to either the right or the left edge of actor '%s'", edge, _clutter_actor_get_debug_name (actor), _clutter_actor_get_debug_name (source)); }
static void clutter_group_real_paint (ClutterActor *actor) { ClutterGroupPrivate *priv = CLUTTER_GROUP (actor)->priv; CLUTTER_NOTE (PAINT, "ClutterGroup paint enter '%s'", _clutter_actor_get_debug_name (actor)); g_list_foreach (priv->children, (GFunc) clutter_actor_paint, NULL); CLUTTER_NOTE (PAINT, "ClutterGroup paint leave '%s'", _clutter_actor_get_debug_name (actor)); }
static gboolean handle_wm_protocols_event (ClutterBackendX11 *backend_x11, ClutterStageX11 *stage_x11, XEvent *xevent) { Atom atom = (Atom) xevent->xclient.data.l[0]; if (atom == backend_x11->atom_WM_DELETE_WINDOW && xevent->xany.window == stage_x11->xwin) { #ifdef CLUTTER_ENABLE_DEBUG ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11); /* the WM_DELETE_WINDOW is a request: we do not destroy * the window right away, as it might contain vital data; * we relay the event to the application and we let it * handle the request */ CLUTTER_NOTE (EVENT, "Delete stage %s[%p], win:0x%x", _clutter_actor_get_debug_name (CLUTTER_ACTOR (stage_cogl->wrapper)), stage_cogl->wrapper, (unsigned int) stage_x11->xwin); #endif /* CLUTTER_ENABLE_DEBUG */ set_user_time (backend_x11, stage_x11, xevent->xclient.data.l[1]); return TRUE; } else if (atom == backend_x11->atom_NET_WM_PING && xevent->xany.window == stage_x11->xwin) { XClientMessageEvent xclient = xevent->xclient; xclient.window = backend_x11->xwin_root; XSendEvent (backend_x11->xdpy, xclient.window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent *) &xclient); return FALSE; } /* do not send any of the WM_PROTOCOLS events to the queue */ return FALSE; }
static void clutter_clone_paint (ClutterActor *actor) { ClutterClone *self = CLUTTER_CLONE (actor); ClutterClonePrivate *priv = self->priv; gboolean was_unmapped = FALSE; if (priv->clone_source == NULL) return; CLUTTER_NOTE (PAINT, "painting clone actor '%s'", _clutter_actor_get_debug_name (actor)); /* The final bits of magic: * - We need to override the paint opacity of the actor with our own * opacity. * - We need to inform the actor that it's in a clone paint (for the function * clutter_actor_is_in_clone_paint()) * - We need to stop clutter_actor_paint applying the model view matrix of * the clone source actor. */ _clutter_actor_set_in_clone_paint (priv->clone_source, TRUE); _clutter_actor_set_opacity_override (priv->clone_source, clutter_actor_get_paint_opacity (actor)); _clutter_actor_set_enable_model_view_transform (priv->clone_source, FALSE); if (!CLUTTER_ACTOR_IS_MAPPED (priv->clone_source)) { _clutter_actor_set_enable_paint_unmapped (priv->clone_source, TRUE); was_unmapped = TRUE; } _clutter_actor_push_clone_paint (); clutter_actor_paint (priv->clone_source); _clutter_actor_pop_clone_paint (); if (was_unmapped) _clutter_actor_set_enable_paint_unmapped (priv->clone_source, FALSE); _clutter_actor_set_enable_model_view_transform (priv->clone_source, TRUE); _clutter_actor_set_opacity_override (priv->clone_source, -1); _clutter_actor_set_in_clone_paint (priv->clone_source, FALSE); }
static ClutterTranslateReturn clutter_stage_x11_translate_event (ClutterEventTranslator *translator, gpointer native, ClutterEvent *event) { ClutterStageX11 *stage_x11; ClutterStageCogl *stage_cogl; ClutterTranslateReturn res = CLUTTER_TRANSLATE_CONTINUE; ClutterBackendX11 *backend_x11; Window stage_xwindow; XEvent *xevent = native; ClutterStage *stage; stage_cogl = clutter_x11_get_stage_window_from_window (xevent->xany.window); if (stage_cogl == NULL) return CLUTTER_TRANSLATE_CONTINUE; stage = stage_cogl->wrapper; stage_x11 = CLUTTER_STAGE_X11 (stage_cogl); backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend); stage_xwindow = stage_x11->xwin; switch (xevent->type) { case ConfigureNotify: if (!stage_x11->is_foreign_xwin) { gboolean size_changed = FALSE; CLUTTER_NOTE (BACKEND, "ConfigureNotify[%x] (%d, %d)", (unsigned int) stage_x11->xwin, xevent->xconfigure.width, xevent->xconfigure.height); /* When fullscreen, we'll keep the xwin_width/height variables to track the old size of the window and we'll assume all ConfigureNotifies constitute a size change */ if (_clutter_stage_is_fullscreen (stage)) size_changed = TRUE; else if ((stage_x11->xwin_width != xevent->xconfigure.width) || (stage_x11->xwin_height != xevent->xconfigure.height)) { size_changed = TRUE; stage_x11->xwin_width = xevent->xconfigure.width; stage_x11->xwin_height = xevent->xconfigure.height; } clutter_actor_set_size (CLUTTER_ACTOR (stage), xevent->xconfigure.width, xevent->xconfigure.height); CLUTTER_UNSET_PRIVATE_FLAGS (stage_cogl->wrapper, CLUTTER_IN_RESIZE); if (size_changed) { /* XXX: This is a workaround for a race condition when * resizing windows while there are in-flight * glXCopySubBuffer blits happening. * * The problem stems from the fact that rectangles for the * blits are described relative to the bottom left of the * window and because we can't guarantee control over the X * window gravity used when resizing so the gravity is * typically NorthWest not SouthWest. * * This means if you grow a window vertically the server * will make sure to place the old contents of the window * at the top-left/north-west of your new larger window, but * that may happen asynchronous to GLX preparing to do a * blit specified relative to the bottom-left/south-west of * the window (based on the old smaller window geometry). * * When the GLX issued blit finally happens relative to the * new bottom of your window, the destination will have * shifted relative to the top-left where all the pixels you * care about are so it will result in a nasty artefact * making resizing look very ugly! * * We can't currently fix this completely, in-part because * the window manager tends to trample any gravity we might * set. This workaround instead simply disables blits for a * while if we are notified of any resizes happening so if * the user is resizing a window via the window manager then * they may see an artefact for one frame but then we will * fallback to redrawing the full stage until the cooling * off period is over. */ if (stage_x11->clipped_redraws_cool_off) g_source_remove (stage_x11->clipped_redraws_cool_off); stage_x11->clipped_redraws_cool_off = clutter_threads_add_timeout (1000, clipped_redraws_cool_off_cb, stage_x11); /* Queue a relayout - we want glViewport to be called * with the correct values, and this is done in ClutterStage * via cogl_onscreen_clutter_backend_set_size (). * * We queue a relayout, because if this ConfigureNotify is * in response to a size we set in the application, the * set_size() call above is essentially a null-op. * * Make sure we do this only when the size has changed, * otherwise we end up relayouting on window moves. */ clutter_actor_queue_relayout (CLUTTER_ACTOR (stage)); /* the resize process is complete, so we can ask the stage * to set up the GL viewport with the new size */ clutter_stage_ensure_viewport (stage); } } break; case PropertyNotify: if (xevent->xproperty.atom == backend_x11->atom_NET_WM_STATE && xevent->xproperty.window == stage_xwindow && !stage_x11->is_foreign_xwin) { Atom type; gint format; gulong n_items, bytes_after; guchar *data = NULL; gboolean fullscreen_set = FALSE; clutter_x11_trap_x_errors (); XGetWindowProperty (backend_x11->xdpy, stage_xwindow, backend_x11->atom_NET_WM_STATE, 0, G_MAXLONG, False, XA_ATOM, &type, &format, &n_items, &bytes_after, &data); clutter_x11_untrap_x_errors (); if (type != None && data != NULL) { gboolean is_fullscreen = FALSE; Atom *atoms = (Atom *) data; gulong i; for (i = 0; i < n_items; i++) { if (atoms[i] == backend_x11->atom_NET_WM_STATE_FULLSCREEN) fullscreen_set = TRUE; } is_fullscreen = _clutter_stage_is_fullscreen (stage_cogl->wrapper); if (fullscreen_set != is_fullscreen) { if (fullscreen_set) _clutter_stage_update_state (stage_cogl->wrapper, 0, CLUTTER_STAGE_STATE_FULLSCREEN); else _clutter_stage_update_state (stage_cogl->wrapper, CLUTTER_STAGE_STATE_FULLSCREEN, 0); } XFree (data); } } break; case FocusIn: if (!_clutter_stage_is_activated (stage_cogl->wrapper)) { _clutter_stage_update_state (stage_cogl->wrapper, 0, CLUTTER_STAGE_STATE_ACTIVATED); } break; case FocusOut: if (_clutter_stage_is_activated (stage_cogl->wrapper)) { _clutter_stage_update_state (stage_cogl->wrapper, CLUTTER_STAGE_STATE_ACTIVATED, 0); } break; case EnterNotify: #if HAVE_XFIXES if (!stage_x11->is_cursor_visible && !stage_x11->cursor_hidden_xfixes) { XFixesHideCursor (backend_x11->xdpy, stage_x11->xwin); stage_x11->cursor_hidden_xfixes = TRUE; } #endif break; case LeaveNotify: #if HAVE_XFIXES if (stage_x11->cursor_hidden_xfixes) { XFixesShowCursor (backend_x11->xdpy, stage_x11->xwin); stage_x11->cursor_hidden_xfixes = FALSE; } #endif break; case Expose: { XExposeEvent *expose = (XExposeEvent *) xevent; cairo_rectangle_int_t clip; CLUTTER_NOTE (EVENT, "expose for stage: %s[%p], win:0x%x - " "redrawing area (x: %d, y: %d, width: %d, height: %d)", _clutter_actor_get_debug_name (CLUTTER_ACTOR (stage)), stage, (unsigned int) stage_xwindow, expose->x, expose->y, expose->width, expose->height); clip.x = expose->x; clip.y = expose->y; clip.width = expose->width; clip.height = expose->height; clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stage), &clip); } break; case DestroyNotify: CLUTTER_NOTE (EVENT, "Destroy notification received for stage %s[%p], win:0x%x", _clutter_actor_get_debug_name (CLUTTER_ACTOR (stage)), stage, (unsigned int) stage_xwindow); event->any.type = CLUTTER_DESTROY_NOTIFY; event->any.stage = stage; res = CLUTTER_TRANSLATE_QUEUE; break; case ClientMessage: CLUTTER_NOTE (EVENT, "Client message for stage %s[%p], win:0x%x", _clutter_actor_get_debug_name (CLUTTER_ACTOR (stage)), stage, (unsigned int) stage_xwindow); if (handle_wm_protocols_event (backend_x11, stage_x11, xevent)) { event->any.type = CLUTTER_DELETE; event->any.stage = stage; res = CLUTTER_TRANSLATE_QUEUE; } break; case MappingNotify: CLUTTER_NOTE (EVENT, "Refresh keyboard mapping"); XRefreshKeyboardMapping (&xevent->xmapping); backend_x11->keymap_serial += 1; res = CLUTTER_TRANSLATE_REMOVE; break; default: res = CLUTTER_TRANSLATE_CONTINUE; break; } return res; }
/** * clutter_gdk_handle_event: * @event: a #GdkEvent * * This function processes a single GDK event; it can be used to hook * into external event processing * * Return value: #GdkFilterReturn. %GDK_FILTER_REMOVE indicates that * Clutter has internally handled the event and the caller should do * no further processing. %GDK_FILTER_CONTINUE indicates that Clutter * is either not interested in the event, or has used the event to * update internal state without taking any exclusive action. * %GDK_FILTER_TRANSLATE will not occur. * */ GdkFilterReturn clutter_gdk_handle_event (GdkEvent *gdk_event) { ClutterDeviceManager *device_manager; ClutterBackendGdk *backend_gdk; ClutterBackend *backend; ClutterStage *stage = NULL; ClutterEvent *event = NULL; gint spin = 0; GdkFilterReturn result = GDK_FILTER_CONTINUE; ClutterInputDevice *device, *source_device; GdkDevice *gdk_device; backend = clutter_get_default_backend (); if (!CLUTTER_IS_BACKEND_GDK (backend)) return GDK_FILTER_CONTINUE; if (gdk_event->any.window == NULL) return GDK_FILTER_CONTINUE; device_manager = clutter_device_manager_get_default (); if (G_UNLIKELY (device_manager == NULL)) return GDK_FILTER_CONTINUE; backend_gdk = CLUTTER_BACKEND_GDK (backend); stage = clutter_gdk_get_stage_from_window (gdk_event->any.window); gdk_device = gdk_event_get_device (gdk_event); if (gdk_device != NULL) device = _clutter_device_manager_gdk_lookup_device (device_manager, gdk_device); else device = NULL; gdk_device = gdk_event_get_source_device (gdk_event); if (gdk_device != NULL) source_device = _clutter_device_manager_gdk_lookup_device (device_manager, gdk_device); else source_device = NULL; if (stage == NULL) return GDK_FILTER_CONTINUE; _clutter_threads_acquire_lock (); switch (gdk_event->type) { case GDK_DELETE: event = clutter_event_new (CLUTTER_DELETE); break; case GDK_DESTROY: event = clutter_event_new (CLUTTER_DESTROY_NOTIFY); break; case GDK_EXPOSE: { ClutterPaintVolume clip; ClutterVertex origin; CLUTTER_NOTE (EVENT, "Expose for stage '%s' [%p] { %d, %d - %d x %d }", _clutter_actor_get_debug_name (CLUTTER_ACTOR (stage)), stage, gdk_event->expose.area.x, gdk_event->expose.area.y, gdk_event->expose.area.width, gdk_event->expose.area.height); origin.x = gdk_event->expose.area.x; origin.y = gdk_event->expose.area.y; origin.z = 0; _clutter_paint_volume_init_static (&clip, CLUTTER_ACTOR (stage)); clutter_paint_volume_set_origin (&clip, &origin); clutter_paint_volume_set_width (&clip, gdk_event->expose.area.width); clutter_paint_volume_set_height (&clip, gdk_event->expose.area.height); _clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stage), 0, &clip); clutter_paint_volume_free (&clip); } break; case GDK_DAMAGE: /* This is handled by cogl */ break; case GDK_MOTION_NOTIFY: event = clutter_event_new (CLUTTER_MOTION); event->motion.time = gdk_event->motion.time; event->motion.x = gdk_event->motion.x; event->motion.y = gdk_event->motion.y; event->motion.axes = NULL; /* It's all X in the end, right? */ event->motion.modifier_state = gdk_event->motion.state; clutter_event_set_device (event, device); clutter_event_set_source_device (event, source_device); CLUTTER_NOTE (EVENT, "Motion notifiy [%.2f, %.2f]", event->motion.x, event->motion.y); break; case GDK_BUTTON_PRESS: case GDK_BUTTON_RELEASE: event = clutter_event_new (gdk_event->type == GDK_BUTTON_PRESS ? CLUTTER_BUTTON_PRESS : CLUTTER_BUTTON_RELEASE); event->button.time = gdk_event->button.time; event->button.x = gdk_event->button.x; event->button.y = gdk_event->button.y; event->button.axes = NULL; event->button.modifier_state = gdk_event->button.state; event->button.button = gdk_event->button.button; event->button.click_count = 1; clutter_event_set_device (event, device); clutter_event_set_source_device (event, source_device); CLUTTER_NOTE (EVENT, "Button %d %s [%.2f, %.2f]", event->button.button, event->type == CLUTTER_BUTTON_PRESS ? "press" : "release", event->button.x, event->button.y); break; case GDK_2BUTTON_PRESS: case GDK_3BUTTON_PRESS: /* these are handled by clutter-main.c updating click_count */ break; case GDK_KEY_PRESS: case GDK_KEY_RELEASE: event = clutter_event_new (gdk_event->type == GDK_KEY_PRESS ? CLUTTER_KEY_PRESS : CLUTTER_KEY_RELEASE); event->key.time = gdk_event->key.time; event->key.modifier_state = gdk_event->key.state; event->key.keyval = gdk_event->key.keyval; event->key.hardware_keycode = gdk_event->key.hardware_keycode; event->key.unicode_value = g_utf8_get_char (gdk_event->key.string); clutter_event_set_device (event, device); clutter_event_set_source_device (event, source_device); CLUTTER_NOTE (EVENT, "Key %d %s", event->key.keyval, event->type == CLUTTER_KEY_PRESS ? "press" : "release"); break; case GDK_ENTER_NOTIFY: case GDK_LEAVE_NOTIFY: event = clutter_event_new (gdk_event->type == GDK_ENTER_NOTIFY ? CLUTTER_ENTER : CLUTTER_LEAVE); event->crossing.source = CLUTTER_ACTOR (stage); event->crossing.time = gdk_event_get_time (gdk_event); event->crossing.x = gdk_event->crossing.x; event->crossing.y = gdk_event->crossing.y; /* XXX: no better fallback here? */ clutter_event_set_device (event, device); clutter_event_set_source_device (event, source_device); if (gdk_event->type == GDK_ENTER_NOTIFY) _clutter_input_device_set_stage (clutter_event_get_device (event), stage); else _clutter_input_device_set_stage (clutter_event_get_device (event), NULL); CLUTTER_NOTE (EVENT, "Crossing %s [%.2f, %.2f]", event->type == CLUTTER_ENTER ? "enter" : "leave", event->crossing.x, event->crossing.y); break; case GDK_FOCUS_CHANGE: if (gdk_event->focus_change.in) _clutter_stage_update_state (stage, 0, CLUTTER_STAGE_STATE_ACTIVATED); else _clutter_stage_update_state (stage, CLUTTER_STAGE_STATE_ACTIVATED, 0); break; case GDK_CONFIGURE: { gfloat w, h; clutter_actor_get_size (CLUTTER_ACTOR (stage), &w, &h); if (w != gdk_event->configure.width || h != gdk_event->configure.height) { clutter_actor_set_size (CLUTTER_ACTOR (stage), gdk_event->configure.width, gdk_event->configure.height); } } break; case GDK_SCROLL: event = clutter_event_new (CLUTTER_SCROLL); event->scroll.time = gdk_event->scroll.time; event->scroll.x = gdk_event->scroll.x; event->scroll.y = gdk_event->scroll.y; event->scroll.modifier_state = gdk_event->scroll.state; event->scroll.axes = NULL; /* XXX: must keep ClutterScrollDirection compatible with GdkScrollDirection */ event->scroll.direction = (ClutterScrollDirection) gdk_event->scroll.direction; clutter_event_set_device (event, device); clutter_event_set_source_device (event, source_device); clutter_event_set_scroll_delta (event, gdk_event->scroll.delta_x, gdk_event->scroll.delta_y); break; case GDK_WINDOW_STATE: if (gdk_event->window_state.changed_mask & GDK_WINDOW_STATE_FULLSCREEN) { gboolean is_fullscreen; is_fullscreen = (gdk_event->window_state.new_window_state & GDK_WINDOW_STATE_FULLSCREEN) != 0; if (is_fullscreen) _clutter_stage_update_state (stage, 0, CLUTTER_STAGE_STATE_FULLSCREEN); else _clutter_stage_update_state (stage, CLUTTER_STAGE_STATE_FULLSCREEN, 0); } break; case GDK_SETTING: _clutter_backend_gdk_update_setting (backend_gdk, gdk_event->setting.name); break; default: break; } if (event != NULL) { event->any.stage = stage; if (gdk_event->any.send_event) event->any.flags = CLUTTER_EVENT_FLAG_SYNTHETIC; _clutter_event_push (event, FALSE); spin = 1; CLUTTER_NOTE (EVENT, "Translated one event from Gdk"); /* handle also synthetic enter/leave events */ if (event->type == CLUTTER_MOTION) spin += 2; while (spin > 0 && (event = clutter_event_get ())) { /* forward the event into clutter for emission etc. */ clutter_do_event (event); clutter_event_free (event); --spin; } result = GDK_FILTER_REMOVE; } _clutter_threads_release_lock (); return result; }