Esempio n. 1
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;
}
Esempio n. 2
0
static gboolean
event_translate (ClutterBackend *backend,
                 ClutterEvent   *event,
                 XEvent         *xevent)
{
  ClutterBackendX11 *backend_x11;
  ClutterStageX11 *stage_x11;
  ClutterStage *stage;
  ClutterStageWindow *impl;
  ClutterDeviceManager *manager;
  gboolean res, not_yet_handled = FALSE;
  Window xwindow, stage_xwindow;
  ClutterInputDevice *device;

  backend_x11 = CLUTTER_BACKEND_X11 (backend);

  xwindow = xevent->xany.window;

  if (backend_x11->event_filters)
    {
      GSList *node;

      node = backend_x11->event_filters;

      while (node)
        {
          ClutterX11EventFilter *filter = node->data;

          switch (filter->func (xevent, event, filter->data))
            {
            case CLUTTER_X11_FILTER_CONTINUE:
              break;
            case CLUTTER_X11_FILTER_TRANSLATE:
              return TRUE;
            case CLUTTER_X11_FILTER_REMOVE:
              return FALSE;
            default:
              break;
            }

          node = node->next;
        }
    }

  /* Do further processing only on events for the stage window (the x11
   * filters might be getting events for other windows, so do not mess
   * them about.
   */
  stage = clutter_x11_get_stage_from_window (xwindow);
  if (stage == NULL)
    return FALSE;

  impl = _clutter_stage_get_window (stage);
  stage_x11 = CLUTTER_STAGE_X11 (impl);
  stage_xwindow = xwindow; /* clutter_x11_get_stage_window (stage); */

  event->any.stage = stage;

  res = TRUE;

  update_last_event_time (backend_x11, xevent);

  manager = clutter_device_manager_get_default ();

  switch (xevent->type)
    {
    case ConfigureNotify:
      if (!stage_x11->is_foreign_xwin)
        {
          CLUTTER_NOTE (BACKEND, "%s: ConfigureNotify[%x] (%d, %d)",
                        G_STRLOC,
                        (unsigned int) stage_x11->xwin,
                        xevent->xconfigure.width,
                        xevent->xconfigure.height);

          /* 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 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.
           */
          if ((stage_x11->state & CLUTTER_STAGE_STATE_FULLSCREEN) ||
              (stage_x11->xwin_width != xevent->xconfigure.width) ||
              (stage_x11->xwin_height != xevent->xconfigure.height))
          clutter_actor_queue_relayout (CLUTTER_ACTOR (stage));

          /* If we're fullscreened, we want these variables to
           * represent the size of the window before it was set
           * to fullscreen.
           */
          if (!(stage_x11->state & CLUTTER_STAGE_STATE_FULLSCREEN))
            {
              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_x11->wrapper,
                                       CLUTTER_STAGE_IN_RESIZE);

          /* 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);
        }
      res = FALSE;
      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)
            {
              Atom *atoms = (Atom *) data;
              gulong i;
              gboolean is_fullscreen = FALSE;

              for (i = 0; i < n_items; i++)
                {
                  if (atoms[i] == backend_x11->atom_NET_WM_STATE_FULLSCREEN)
                    fullscreen_set = TRUE;
                }

              is_fullscreen =
                (stage_x11->state & CLUTTER_STAGE_STATE_FULLSCREEN);

              if (fullscreen_set != is_fullscreen)
                {
                  if (fullscreen_set)
                    stage_x11->state |= CLUTTER_STAGE_STATE_FULLSCREEN;
                  else
                    stage_x11->state &= ~CLUTTER_STAGE_STATE_FULLSCREEN;

                  stage_x11->fullscreening = fullscreen_set;

                  event->type = CLUTTER_STAGE_STATE;
                  event->stage_state.changed_mask =
                    CLUTTER_STAGE_STATE_FULLSCREEN;
                  event->stage_state.new_state = stage_x11->state;
                }
              else
                res = FALSE;

              XFree (data);
            }
          else
            res = FALSE;
        }
      else
        res = FALSE;
      break;

    case MapNotify:
      res = FALSE;
      break;

    case UnmapNotify:
      res = FALSE;
      break;

    case FocusIn:
      if (!(stage_x11->state & CLUTTER_STAGE_STATE_ACTIVATED))
        {
          /* TODO: check xevent->xfocus.detail ? */
          stage_x11->state |= CLUTTER_STAGE_STATE_ACTIVATED;

          event->type = CLUTTER_STAGE_STATE;
          event->stage_state.changed_mask = CLUTTER_STAGE_STATE_ACTIVATED;
          event->stage_state.new_state = stage_x11->state;
        }
      else
        res = FALSE;
      break;

    case FocusOut:
      if (stage_x11->state & CLUTTER_STAGE_STATE_ACTIVATED)
        {
          stage_x11->state &= ~CLUTTER_STAGE_STATE_ACTIVATED;

          event->type = CLUTTER_STAGE_STATE;
          event->stage_state.changed_mask = CLUTTER_STAGE_STATE_ACTIVATED;
          event->stage_state.new_state = stage_x11->state;
        }
      else
        res = FALSE;
      break;

    case Expose:
      {
        CLUTTER_NOTE (MULTISTAGE, "expose for stage: %p, redrawing", stage);
        clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
        res = FALSE;
      }
      break;
    case DestroyNotify:
      CLUTTER_NOTE (EVENT, "destroy notify:\txid: %ld",
                    xevent->xdestroywindow.window);
      if (xevent->xdestroywindow.window == stage_xwindow &&
          !stage_x11->is_foreign_xwin)
        event->type = event->any.type = CLUTTER_DESTROY_NOTIFY;
      else
        res = FALSE;
      break;

    case ClientMessage:
      CLUTTER_NOTE (EVENT, "client message");

      event->type = event->any.type = CLUTTER_CLIENT_MESSAGE;

      if (xevent->xclient.message_type == backend_x11->atom_XEMBED)
        res = handle_xembed_event (backend_x11, xevent);
      else if (xevent->xclient.message_type == backend_x11->atom_WM_PROTOCOLS)
        {
          res = handle_wm_protocols_event (backend_x11, stage_xwindow, xevent);
          event->type = event->any.type = CLUTTER_DELETE;
        }
      break;

    case KeyPress:
      event->key.type = event->type = CLUTTER_KEY_PRESS;
      event->key.device =
        clutter_device_manager_get_core_device (manager,
                                                CLUTTER_KEYBOARD_DEVICE);

      translate_key_event (backend, event, xevent);

      set_user_time (backend_x11, &xwindow, xevent->xkey.time);
      break;
              
    case KeyRelease:
      /* old-style X11 terminals require that even modern X11 send
       * KeyPress/KeyRelease pairs when auto-repeating. for this
       * reason modern(-ish) API like XKB has a way to detect
       * auto-repeat and do a single KeyRelease at the end of a
       * KeyPress sequence.
       *
       * this check emulates XKB's detectable auto-repeat; we peek
       * the next event and check if it's a KeyPress for the same key
       * and timestamp - and then ignore it if it matches the
       * KeyRelease
       */
      if (XPending (xevent->xkey.display))
        {
          XEvent next_event;

          XPeekEvent (xevent->xkey.display, &next_event);

          if (next_event.type == KeyPress &&
              next_event.xkey.keycode == xevent->xkey.keycode &&
              next_event.xkey.time == xevent->xkey.time)
            {
              res = FALSE;
              break;
            }
        }

      event->key.type = event->type = CLUTTER_KEY_RELEASE;
      event->key.device =
        clutter_device_manager_get_core_device (manager,
                                                CLUTTER_KEYBOARD_DEVICE);

      translate_key_event (backend, event, xevent);
      break;

    default:
      /* ignore every other event */
      not_yet_handled = TRUE;
      break;
    }

  /* Input device event handling.. */
  if (not_yet_handled)
    {
      device = clutter_device_manager_get_core_device (manager,
                                                       CLUTTER_POINTER_DEVICE);

      /* Regular X event */
      switch (xevent->type)
        {
        case ButtonPress:
          switch (xevent->xbutton.button)
            {
            case 4: /* up */
            case 5: /* down */
            case 6: /* left */
            case 7: /* right */
              event->scroll.type = event->type = CLUTTER_SCROLL;

              if (xevent->xbutton.button == 4)
                event->scroll.direction = CLUTTER_SCROLL_UP;
              else if (xevent->xbutton.button == 5)
                event->scroll.direction = CLUTTER_SCROLL_DOWN;
              else if (xevent->xbutton.button == 6)
                event->scroll.direction = CLUTTER_SCROLL_LEFT;
              else
                event->scroll.direction = CLUTTER_SCROLL_RIGHT;

              event->scroll.time = xevent->xbutton.time;
              event->scroll.x = xevent->xbutton.x;
              event->scroll.y = xevent->xbutton.y;
              event->scroll.modifier_state = xevent->xbutton.state;
              event->scroll.device = device;
              break;

            default:
              event->button.type = event->type = CLUTTER_BUTTON_PRESS;
              event->button.time = xevent->xbutton.time;
              event->button.x = xevent->xbutton.x;
              event->button.y = xevent->xbutton.y;
              event->button.modifier_state = xevent->xbutton.state;
              event->button.button = xevent->xbutton.button;
              event->button.device = device;
              break;
            }

          set_user_time (backend_x11, &xwindow, event->button.time);

          res = TRUE;
          break;

        case ButtonRelease:
          /* scroll events don't have a corresponding release */
          if (xevent->xbutton.button == 4 ||
              xevent->xbutton.button == 5 ||
              xevent->xbutton.button == 6 ||
              xevent->xbutton.button == 7)
            {
              res = FALSE;
              goto out;
            }

          event->button.type = event->type = CLUTTER_BUTTON_RELEASE;
          event->button.time = xevent->xbutton.time;
          event->button.x = xevent->xbutton.x;
          event->button.y = xevent->xbutton.y;
          event->button.modifier_state = xevent->xbutton.state;
          event->button.button = xevent->xbutton.button;
          event->button.device = device;

          res = TRUE;
          break;

        case MotionNotify:
          event->motion.type = event->type = CLUTTER_MOTION;
          event->motion.time = xevent->xmotion.time;
          event->motion.x = xevent->xmotion.x;
          event->motion.y = xevent->xmotion.y;
          event->motion.modifier_state = xevent->xmotion.state;
          event->motion.device = device;

          res = TRUE;
          break;

        case EnterNotify:
          /* we know that we are entering the stage here */
          _clutter_input_device_set_stage (device, stage);
          CLUTTER_NOTE (EVENT, "Entering the stage");

          /* Convert enter notifies to motion events because X
             doesn't emit the corresponding motion notify */
          event->motion.type = event->type = CLUTTER_MOTION;
          event->motion.time = xevent->xcrossing.time;
          event->motion.x = xevent->xcrossing.x;
          event->motion.y = xevent->xcrossing.y;
          event->motion.modifier_state = xevent->xcrossing.state;
          event->motion.source = CLUTTER_ACTOR (stage);
          event->motion.device = device;

          res = TRUE;
          break;

        case LeaveNotify:
          if (device->stage == NULL)
            {
              CLUTTER_NOTE (EVENT,
                            "Discarding LeaveNotify for ButtonRelease "
                            "event off-stage");
              res = FALSE;
              goto out;
            }

          /* we know that we are leaving the stage here */
          _clutter_input_device_set_stage (device, NULL);
          CLUTTER_NOTE (EVENT, "Leaving the stage (time:%u)",
                        event->crossing.time);

          event->crossing.type = event->type = CLUTTER_LEAVE;
          event->crossing.time = xevent->xcrossing.time;
          event->crossing.x = xevent->xcrossing.x;
          event->crossing.y = xevent->xcrossing.y;
          event->crossing.source = CLUTTER_ACTOR (stage);
          event->crossing.device = device;
          res = TRUE;
          break;

        default:
          res = FALSE;
          break;
        }
    }

    /* XInput fun...*/
  if (!res && clutter_x11_has_xinput ())
    {
#ifdef HAVE_XINPUT
      int *ev_types = backend_x11->event_types;
      int button_press, button_release;
      int key_press, key_release;
      int motion_notify;

      button_press   = ev_types[CLUTTER_X11_XINPUT_BUTTON_PRESS_EVENT];
      button_release = ev_types[CLUTTER_X11_XINPUT_BUTTON_RELEASE_EVENT];
      motion_notify  = ev_types[CLUTTER_X11_XINPUT_MOTION_NOTIFY_EVENT];
      key_press      = ev_types[CLUTTER_X11_XINPUT_KEY_PRESS_EVENT];
      key_release    = ev_types[CLUTTER_X11_XINPUT_KEY_RELEASE_EVENT];

      CLUTTER_NOTE (EVENT, "XInput event type: %d", xevent->type);

      if (xevent->type == button_press)
        {
          XDeviceButtonEvent *xbev = (XDeviceButtonEvent *) xevent;

          device = _clutter_x11_get_device_for_xid (xbev->deviceid);
          _clutter_input_device_set_stage (device, stage);

          CLUTTER_NOTE (EVENT,
                        "XI ButtonPress for %li ('%s') at %d, %d",
                        xbev->deviceid,
                        device->device_name,
                        xbev->x,
                        xbev->y);

          switch (xbev->button)
            {
            case 4:
            case 5:
            case 6:
            case 7:
              event->scroll.type = event->type = CLUTTER_SCROLL;

              if (xbev->button == 4)
                event->scroll.direction = CLUTTER_SCROLL_UP;
              else if (xbev->button == 5)
                event->scroll.direction = CLUTTER_SCROLL_DOWN;
              else if (xbev->button == 6)
                event->scroll.direction = CLUTTER_SCROLL_LEFT;
              else
                event->scroll.direction = CLUTTER_SCROLL_RIGHT;

              event->scroll.time = xbev->time;
              event->scroll.x = xbev->x;
              event->scroll.y = xbev->y;
              event->scroll.modifier_state = xbev->state;
              event->scroll.device = device;
              break;

            default:
              event->button.type = event->type = CLUTTER_BUTTON_PRESS;
              event->button.time = xbev->time;
              event->button.x = xbev->x;
              event->button.y = xbev->y;
              event->button.modifier_state = xbev->state;
              event->button.button = xbev->button;
              event->button.device = device;
              break;
            }

          set_user_time (backend_x11, &xwindow, xbev->time);

          res = TRUE;
        }
      else if (xevent->type == button_release)
        {
          XDeviceButtonEvent *xbev = (XDeviceButtonEvent *)xevent;

          device = _clutter_x11_get_device_for_xid (xbev->deviceid);
          _clutter_input_device_set_stage (device, stage);

          CLUTTER_NOTE (EVENT, "XI ButtonRelease for %li ('%s') at %d, %d",
                        xbev->deviceid,
                        device->device_name,
                        xbev->x,
                        xbev->y);

          /* scroll events don't have a corresponding release */
          if (xbev->button == 4 ||
              xbev->button == 5 ||
              xbev->button == 6 ||
              xbev->button == 7)
            {
              res = FALSE;
              goto out;
            }

          event->button.type = event->type = CLUTTER_BUTTON_RELEASE;
          event->button.time = xbev->time;
          event->button.x = xbev->x;
          event->button.y = xbev->y;
          event->button.modifier_state = xbev->state;
          event->button.button = xbev->button;
          event->button.device = device;

          res = TRUE;
        }
      else if (xevent->type == motion_notify)
        {
          XDeviceMotionEvent *xmev = (XDeviceMotionEvent *)xevent;

          device = _clutter_x11_get_device_for_xid (xmev->deviceid);
          _clutter_input_device_set_stage (device, stage);

          CLUTTER_NOTE (EVENT, "XI Motion for %li ('%s') at %d, %d",
                        xmev->deviceid,
                        device->device_name,
                        xmev->x,
                        xmev->y);

          event->motion.type = event->type = CLUTTER_MOTION;
          event->motion.time = xmev->time;
          event->motion.x = xmev->x;
          event->motion.y = xmev->y;
          event->motion.modifier_state = xmev->state;
          event->motion.device = device;

          res = TRUE;
        }
      else if (xevent->type == key_press || xevent->type == key_release)
        {
          /* the XInput 1.x handling of key presses/releases is broken:
           * it makes key repeat, key presses and releases outside the
           * window not generate events even when the window has focus
           */
          XDeviceKeyEvent *xkev = (XDeviceKeyEvent *) xevent;
          XEvent xevent_converted;

          convert_xdevicekey_to_xkey (xkev, &xevent_converted);

          event->key.type = event->type = (xevent->type == key_press)
                                          ? CLUTTER_KEY_PRESS
                                          : CLUTTER_KEY_RELEASE;

          translate_key_event (backend, event, &xevent_converted);

          if (xevent->type == key_press)
            set_user_time (backend_x11, &xwindow, xkev->time);
        }
      else
#endif /* HAVE_XINPUT */
        {
          CLUTTER_NOTE (EVENT, "Uknown Event");
          res = FALSE;
        }
    }

out:
  return res;
}