summaryrefslogblamecommitdiff
path: root/src/lib/efl_wl/copiedfromweston.x
blob: 0b1388b0648792560e146ae746b6129b3bf09af4 (plain) (tree)





































































































































































































































































































































































                                                                                                                     
/*
 * Copyright © 2011 Kristian Høgsberg
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#define ALL_ACTIONS (WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY | \
                     WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE | \
                     WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK)

static uint32_t
data_offer_choose_action(Comp_Data_Device_Offer *offer)
{
   uint32_t available_actions, preferred_action = 0;
   uint32_t source_actions, offer_actions;

   if (wl_resource_get_version(offer->res) >=
       WL_DATA_OFFER_ACTION_SINCE_VERSION)
     {
        offer_actions = offer->dnd_actions;
        preferred_action = offer->preferred_dnd_action;
     }
   else
     {
        offer_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
     }

   if (wl_resource_get_version(offer->source->res) >=
       WL_DATA_SOURCE_ACTION_SINCE_VERSION)
     source_actions = offer->source->dnd_actions;
   else
     source_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;

   available_actions = offer_actions & source_actions;

   if (!available_actions)
     return WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;

   if (offer->source->seat &&
       offer->source->compositor_action & available_actions)
     return offer->source->compositor_action;

   /* If the dest side has a preferred DnD action, use it */
   if ((preferred_action & available_actions) != 0)
     return preferred_action;

   /* Use the first found action, in bit order */
   return 1 << (ffs(available_actions) - 1);
}

static void
data_offer_update_action(Comp_Data_Device_Offer *offer)
{
   uint32_t action;

   if (!offer->source)
     return;

   action = data_offer_choose_action(offer);

   if (offer->source->current_dnd_action == action)
     return;

   offer->source->current_dnd_action = action;

   if (offer->in_ask)
     return;

   if (wl_resource_get_version(offer->source->res) >=
       WL_DATA_SOURCE_ACTION_SINCE_VERSION)
     wl_data_source_send_action(offer->source->res, action);

   if (wl_resource_get_version(offer->res) >=
       WL_DATA_OFFER_ACTION_SINCE_VERSION)
     wl_data_offer_send_action(offer->res, action);
}

static void
data_device_offer_set_actions(struct wl_client *client,
                              struct wl_resource *resource,
                              uint32_t dnd_actions, uint32_t preferred_action)
{
   Comp_Data_Device_Offer *offer = wl_resource_get_user_data(resource);

   if (dnd_actions & ~ALL_ACTIONS)
     {
        wl_resource_post_error(offer->res,
                               WL_DATA_OFFER_ERROR_INVALID_ACTION_MASK,
                               "invalid action mask %x", dnd_actions);
        return;
     }

   if (preferred_action &&
       (!(preferred_action & dnd_actions) ||
        __builtin_popcount(preferred_action) > 1))
     {
        wl_resource_post_error(offer->res,
                               WL_DATA_OFFER_ERROR_INVALID_ACTION,
                               "invalid action %x", preferred_action);
        return;
     }

   offer->dnd_actions = dnd_actions;
   offer->preferred_dnd_action = preferred_action;
   data_offer_update_action(offer);
}

#ifdef HAVE_ECORE_X
static Ecore_X_Atom
action_convert(uint32_t action)
{
   if (action == WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE)
     return ECORE_X_ATOM_XDND_ACTION_MOVE;
   if (action == WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK)
     return ECORE_X_ATOM_XDND_ACTION_ASK;
   return ECORE_X_ATOM_XDND_ACTION_COPY;
}
#endif
static void
data_device_offer_accept(struct wl_client *client, struct wl_resource *resource,
                         uint32_t serial, const char *mime_type)
{
   Comp_Data_Device_Offer *offer = wl_resource_get_user_data(resource);
   Comp_Surface *cs;

   /* Protect against untimely calls from older data offers */
   if (!offer->source || offer != offer->source->offer)
     return;

   switch (offer->type)
     {
      case COMP_DATA_DEVICE_OFFER_TYPE_DND:
        cs = offer->source->seat->drag.enter;
        if (!offer->source->seat->drag.res) return;
        if ((!offer->source->seat->drag.source) &&
          (wl_resource_get_client(cs->res) != wl_resource_get_client(offer->source->seat->drag.res)))
          return;
#ifdef HAVE_ECORE_X
        if (offer->source->x11_owner)
          {
             Ecore_Window win = ecore_evas_window_get(ecore_evas_ecore_evas_get(offer->source->seat->c->evas));
             offer->source->accepted = mime_type != NULL;
             ecore_x_client_message32_send(offer->source->x11_owner,
               ECORE_X_ATOM_XDND_STATUS, ECORE_X_EVENT_MASK_NONE,
               win, 2 | !!mime_type, 0, 0,
               (!!mime_type) * action_convert(offer->source->current_dnd_action));
             return;
          }
#endif
        break;

      case COMP_DATA_DEVICE_OFFER_TYPE_CLIPBOARD:
        break;
      default: return;
     }
   if (offer->source->seat->client_offer)
     ecore_wl2_offer_accept(offer->source->seat->client_offer, mime_type);
   else
     wl_data_source_send_target(offer->source->res, mime_type);
   offer->source->accepted = mime_type != NULL;
}

static void
data_device_offer_receive(struct wl_client *client, struct wl_resource *resource,
                          const char *mime_type, int32_t fd)
{
   Comp_Data_Device_Offer *offer = wl_resource_get_user_data(resource);

   if (offer->source && offer == offer->source->offer)
     {
        if (offer->proxy)
          {
             Ecore_Wl2_Offer *off;
#ifdef HAVE_ECORE_X
             if (offer->source->x11_owner)
               {
                  x11_send_send(offer->source, mime_type, fd, offer->type);
                  return;
               }
#endif
             if (offer->type == COMP_DATA_DEVICE_OFFER_TYPE_CLIPBOARD)
               off = ecore_wl2_dnd_selection_get(offer->source->seat->seat);
             else
               {
                  off = offer->source->seat->client_offer;
                  offer->source->seat->client_offer = NULL;
               }
             ecore_wl2_offer_proxy_receive(off, mime_type, fd);
             offer->proxy_offer = off;
          }
        else
          wl_data_source_send_send(offer->source->res, mime_type, fd);
     }
   close(fd);
}

static void
data_source_notify_finish(Comp_Data_Device_Source *source)
{
   if (!source->actions_set)
     return;

   if (source->proxy && (!source->x11_owner))
     ecore_wl2_offer_finish(source->offer->proxy_offer);

   if (source->offer && source->offer->in_ask &&
       wl_resource_get_version(source->res) >=
       WL_DATA_SOURCE_ACTION_SINCE_VERSION)
     {
        wl_data_source_send_action(source->res,
                                   source->current_dnd_action);
     }

   if (wl_resource_get_version(source->res) >=
       WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION)
     {
        wl_data_source_send_dnd_finished(source->res);
     }

   source->offer = NULL;
}

static void
data_device_offer_finish(struct wl_client *client, struct wl_resource *resource)
{
   Comp_Data_Device_Offer *offer = wl_resource_get_user_data(resource);

   if (!offer->source || offer->source->offer != offer)
     return;

   /* Disallow finish while we have a grab driving drag-and-drop, or
    * if the negotiation is not at the right stage
    */
   if (((!offer->proxy) && offer->source->seat) ||
       !offer->source->accepted)
     {
        wl_resource_post_error(offer->res,
                               WL_DATA_OFFER_ERROR_INVALID_FINISH,
                               "premature finish request");
        return;
     }

   switch (offer->source->current_dnd_action)
     {
      case WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE:
      case WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK:
        wl_resource_post_error(offer->res,
                               WL_DATA_OFFER_ERROR_INVALID_OFFER,
                               "offer finished with an invalid action");
        return;

      default:
        break;
     }

   data_source_notify_finish(offer->source);
}

static void
data_device_offer_impl_destroy(struct wl_resource *resource)
{
   Comp_Data_Device_Offer *offer = wl_resource_get_user_data(resource);

   if (!offer->source)
     goto out;

   if (offer->source->offer != offer)
     goto out;

   if (offer->type == COMP_DATA_DEVICE_OFFER_TYPE_DND)
     {
        /* If the drag destination has version < 3, wl_data_offer.finish
         * won't be called, so do this here as a safety net, because
         * we still want the version >=3 drag source to be happy.
         */
        if (wl_resource_get_version(offer->res) <
            WL_DATA_OFFER_ACTION_SINCE_VERSION)
          {
             data_source_notify_finish(offer->source);
          }
        else if (offer->source->res &&
                 wl_resource_get_version(offer->source->res) >=
                 WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION)
          {
             wl_data_source_send_cancelled(offer->source->res);
          }
     }

   offer->source->offer = NULL;
   if (offer->proxy_offer && offer->proxy)
     ecore_wl2_offer_proxy_receive_end(offer->proxy_offer);
out:
   free(offer);
}

static void
drag_grab_button(Comp_Seat *s,
                 uint32_t time, uint32_t button, uint32_t state_w)
{
   Comp_Data_Device_Source *data_source = s->drag.source;
   enum wl_pointer_button_state state = state_w;

   if (data_source &&
       s->drag.id == button &&
       state == WL_POINTER_BUTTON_STATE_RELEASED)
     {
        if ((s->drag.enter || (s->drag.x11_owner == ecore_evas_window_get(ecore_evas_ecore_evas_get(s->c->evas)))) &&
            data_source->accepted &&
            data_source->current_dnd_action)
          {
             if (s->drag.enter)
               wl_data_device_send_drop(data_device_find(s, s->drag.enter->res));

             if (wl_resource_get_version(data_source->res) >=
                 WL_DATA_SOURCE_DND_DROP_PERFORMED_SINCE_VERSION)
               wl_data_source_send_dnd_drop_performed(data_source->res);

             if (data_source->offer)
               data_source->offer->in_ask =
                 data_source->current_dnd_action ==
                 WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK;

             if (!data_source->proxy)
               data_source->seat = NULL;
          }
        else if (wl_resource_get_version(data_source->res) >=
                 WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION)
          {
             wl_data_source_send_cancelled(data_source->res);
          }
        seat_drag_end(s);
        if (!data_source->x11_owner)
          s->drag.source = NULL;
#ifdef HAVE_ECORE_X
        if (ecore_x_display_get())
          ecore_x_pointer_ungrab();
#endif
     }
}