Exemplo n.º 1
0
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);
}
Exemplo n.º 2
0
/**
 * 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]);
}
Exemplo n.º 4
0
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));
}
Exemplo n.º 5
0
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));
}
Exemplo n.º 6
0
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));
}
Exemplo n.º 7
0
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;
}
Exemplo n.º 8
0
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);
}
Exemplo n.º 9
0
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;
}
Exemplo n.º 10
0
/**
 * 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;
}