[PATCH weston v7 2/5] data-device: Implement DnD actions

Carlos Garnacho carlosg at gnome.org
Fri Jan 15 11:57:53 PST 2016


Hey Jonas,

On Fri, Jan 15, 2016 at 7:10 AM, Jonas Ã…dahl <jadahl at gmail.com> wrote:
> On Thu, Jan 14, 2016 at 11:46:34PM +0100, Carlos Garnacho wrote:
>> The policy in weston in order to determine the chosen DnD action is
>> deliberately simple, and is probably the minimals that any compositor
>> should be doing here.
>>
>> Besides honoring the set_actions requests on both wl_data_source and
>> wl_data_offer, weston now will emit the newly added "action" events
>> notifying both source and dest of the chosen action.
>>
>> The "dnd" client has been updated too (although minimally), so it
>> notifies the compositor of a "move" action on both sides.
>>
>> Changes since v6:
>>   - Emit errors as defined in DnD actions patch v10.
>>
>> Changes since v5:
>>   - Use enum types and values for not-a-bitfield stored values.
>>     handle errors when finding unexpected dnd_actions values.
>>
>> Changes since v4:
>>   - Added compositor-side version checks. Spaces vs tabs fixes.
>>     Fixed resource versioning. Initialized new weston_data_source/offer
>>     fields.
>>
>> Changes since v3:
>>   - Put data_source.action to use in the dnd client, now updates
>>     the dnd surface like data_source.target events do.
>>
>> Changes since v2:
>>   - Split from DnD progress notification changes.
>>
>> Changes since v1:
>>   - Updated to v2 of DnD actions protocol changes, implement
>>     wl_data_offer.source_actions.
>>   - Fixed coding style issues.
>>
>> Signed-off-by: Carlos Garnacho <carlosg at gnome.org>
>> Reviewed-by: Michael Catanzaro <mcatanzaro at igalia.com>
>
> Mostly looks good, except for one part regarding the order of
> wl_data_source.set_actions and wl_data_device.start_drag. I also have a
> question regarding what to do with events during "ask". See the comments
> inline.
>
>> ---
>>  clients/dnd.c     |  32 +++++++++--
>>  clients/window.c  |  25 ++++++++
>>  src/compositor.h  |   5 ++
>>  src/data-device.c | 169 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
>>  4 files changed, 221 insertions(+), 10 deletions(-)
>>
>> diff --git a/clients/dnd.c b/clients/dnd.c
>> index 48111d9..ddf3fcc 100644
>> --- a/clients/dnd.c
>> +++ b/clients/dnd.c
>> @@ -72,6 +72,7 @@ struct dnd_drag {
>>       struct item *item;
>>       int x_offset, y_offset;
>>       int width, height;
>> +     uint32_t dnd_action;
>>       const char *mime_type;
>>
>>       struct wl_surface *drag_surface;
>> @@ -266,16 +267,13 @@ dnd_get_item(struct dnd *dnd, int32_t x, int32_t y)
>>  }
>>
>>  static void
>> -data_source_target(void *data,
>> -                struct wl_data_source *source, const char *mime_type)
>> +dnd_drag_update_surface(struct dnd_drag *dnd_drag)
>>  {
>> -     struct dnd_drag *dnd_drag = data;
>>       struct dnd *dnd = dnd_drag->dnd;
>>       cairo_surface_t *surface;
>>       struct wl_buffer *buffer;
>>
>> -     dnd_drag->mime_type = mime_type;
>> -     if (mime_type)
>> +     if (dnd_drag->mime_type && dnd_drag->dnd_action)
>>               surface = dnd_drag->opaque;
>>       else
>>               surface = dnd_drag->translucent;
>> @@ -288,6 +286,16 @@ data_source_target(void *data,
>>  }
>>
>>  static void
>> +data_source_target(void *data,
>> +                struct wl_data_source *source, const char *mime_type)
>> +{
>> +     struct dnd_drag *dnd_drag = data;
>> +
>> +     dnd_drag->mime_type = mime_type;
>> +     dnd_drag_update_surface(dnd_drag);
>> +}
>> +
>> +static void
>>  data_source_send(void *data, struct wl_data_source *source,
>>                const char *mime_type, int32_t fd)
>>  {
>> @@ -360,12 +368,22 @@ data_source_drag_finished(void *data, struct wl_data_source *source)
>>       dnd_drag_destroy(dnd_drag);
>>  }
>>
>> +static void
>> +data_source_action(void *data, struct wl_data_source *source, uint32_t dnd_action)
>> +{
>> +     struct dnd_drag *dnd_drag = data;
>> +
>> +     dnd_drag->dnd_action = dnd_action;
>> +     dnd_drag_update_surface(dnd_drag);
>> +}
>> +
>>  static const struct wl_data_source_listener data_source_listener = {
>>       data_source_target,
>>       data_source_send,
>>       data_source_cancelled,
>>       data_source_drop_performed,
>>       data_source_drag_finished,
>> +     data_source_action,
>>  };
>>
>>  static cairo_surface_t *
>> @@ -428,6 +446,8 @@ create_drag_source(struct dnd *dnd,
>>               dnd_drag->item = item;
>>               dnd_drag->x_offset = x - item->x;
>>               dnd_drag->y_offset = y - item->y;
>> +             dnd_drag->dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
>> +             dnd_drag->mime_type = NULL;
>>
>>               for (i = 0; i < ARRAY_LENGTH(dnd->items); i++) {
>>                       if (item == dnd->items[i]){
>> @@ -461,6 +481,8 @@ create_drag_source(struct dnd *dnd,
>>                                         window_get_wl_surface(dnd->window),
>>                                         dnd_drag->drag_surface,
>>                                         serial);
>> +             wl_data_source_set_actions(dnd_drag->data_source,
>> +                                        WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE);
>>
>>               dnd_drag->opaque =
>>                       create_drag_icon(dnd_drag, item, x, y, 1);
>> diff --git a/clients/window.c b/clients/window.c
>> index c93edb1..db1fe57 100644
>> --- a/clients/window.c
>> +++ b/clients/window.c
>> @@ -3323,6 +3323,8 @@ struct data_offer {
>>       int fd;
>>       data_func_t func;
>>       int32_t x, y;
>> +     uint32_t dnd_action;
>> +     uint32_t source_actions;
>>       void *user_data;
>>  };
>>
>> @@ -3336,8 +3338,26 @@ data_offer_offer(void *data, struct wl_data_offer *wl_data_offer, const char *ty
>>       *p = strdup(type);
>>  }
>>
>> +static void
>> +data_offer_source_actions(void *data, struct wl_data_offer *wl_data_offer, uint32_t source_actions)
>> +{
>> +     struct data_offer *offer = data;
>> +
>> +     offer->source_actions = source_actions;
>> +}
>> +
>> +static void
>> +data_offer_action(void *data, struct wl_data_offer *wl_data_offer, uint32_t dnd_action)
>> +{
>> +     struct data_offer *offer = data;
>> +
>> +     offer->dnd_action = dnd_action;
>> +}
>> +
>>  static const struct wl_data_offer_listener data_offer_listener = {
>>       data_offer_offer,
>> +     data_offer_source_actions,
>> +     data_offer_action
>>  };
>>
>>  static void
>> @@ -3401,6 +3421,11 @@ data_device_enter(void *data, struct wl_data_device *data_device,
>>               *p = NULL;
>>
>>               types_data = input->drag_offer->types.data;
>> +             wl_data_offer_set_actions(offer,
>> +                                       WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY |
>> +                                       WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE,
>> +                                       WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE);
>> +
>>       } else {
>>               input->drag_offer = NULL;
>>               types_data = NULL;
>> diff --git a/src/compositor.h b/src/compositor.h
>> index 8d6b051..c53949e 100644
>> --- a/src/compositor.h
>> +++ b/src/compositor.h
>> @@ -311,6 +311,8 @@ struct weston_data_offer {
>>       struct wl_resource *resource;
>>       struct weston_data_source *source;
>>       struct wl_listener source_destroy_listener;
>> +     uint32_t dnd_actions;
>> +     enum wl_data_device_manager_dnd_action preferred_dnd_action;
>>  };
>>
>>  struct weston_data_source {
>> @@ -320,6 +322,9 @@ struct weston_data_source {
>>       struct weston_data_offer *offer;
>>       struct weston_seat *seat;
>>       bool accepted;
>> +     bool actions_set;
>> +     uint32_t dnd_actions;
>> +     enum wl_data_device_manager_dnd_action current_dnd_action;
>>
>>       void (*accept)(struct weston_data_source *source,
>>                      uint32_t serial, const char *mime_type);
>> diff --git a/src/data-device.c b/src/data-device.c
>> index d2b89bb..18448bf 100644
>> --- a/src/data-device.c
>> +++ b/src/data-device.c
>> @@ -56,6 +56,10 @@ struct weston_touch_drag {
>>       struct weston_touch_grab grab;
>>  };
>>
>> +#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 void
>>  data_offer_accept(struct wl_client *client, struct wl_resource *resource,
>>                 uint32_t serial, const char *mime_type)
>> @@ -107,6 +111,89 @@ data_offer_notify_finish(struct weston_data_offer *offer)
>>       offer->source->offer = NULL;
>>  }
>>
>> +static uint32_t
>> +data_offer_choose_action(struct weston_data_offer *offer)
>> +{
>> +     uint32_t available_actions, preferred_action = 0;
>> +     uint32_t source_actions, offer_actions;
>> +
>> +     if (wl_resource_get_version(offer->resource) >=
>> +         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->resource) >=
>> +         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 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(struct weston_data_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;
>> +
>
> I wonder if we can't avoid sending these, after "ask", and only send a
> final wl_data_source_send_action(..) just before the
> wl_data_source_send_finished(..). This would be done by setting a state
> state on weston_data_offer at the time of
> wl_data_source_send_dnd_drop_performed()/wl_data_device_drop() that the
> offer is now in "ask" mode. If it is, we'd not send any of these actions
> (since they might not represent the correct actual action).
>
> Later in the notify_finish() function, we'd do if
> (offer->is_in_ask_state) wl_data_offer_send_action(...). Any use doing
> this, or should we just let all the events through, even after "ask" was
> the action at the drop? I'm not sure the current spec clarifies whether
> this happens or not.

Right, this was left out. Observed in my next batch of patches.

>
>> +     if (wl_resource_get_version(offer->source->resource) >=
>> +         WL_DATA_SOURCE_ACTION_SINCE_VERSION)
>> +             wl_data_source_send_action(offer->source->resource, action);
>> +
>> +     if (wl_resource_get_version(offer->resource) >=
>> +         WL_DATA_OFFER_ACTION_SINCE_VERSION)
>> +             wl_data_offer_send_action(offer->resource, action);
>> +}
>> +
>> +static void
>> +data_offer_set_actions(struct wl_client *client,
>> +                    struct wl_resource *resource,
>> +                    uint32_t dnd_actions, uint32_t preferred_action)
>> +{
>> +     struct weston_data_offer *offer = wl_resource_get_user_data(resource);
>> +
>> +     if (dnd_actions & ~ALL_ACTIONS) {
>> +             wl_resource_post_error(offer->resource,
>> +                                    WL_DATA_OFFER_ERROR_INVALID_ACTION_MASK,
>> +                                    "invalid action mask %x", dnd_actions);
>> +             return;
>> +     }
>> +
>> +     if (__builtin_popcount(preferred_action) > 1) {
>> +             wl_resource_post_error(offer->resource,
>> +                                    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);
>> +}
>> +
>>  static void
>>  data_offer_finish(struct wl_client *client, struct wl_resource *resource)
>>  {
>> @@ -126,6 +213,15 @@ data_offer_finish(struct wl_client *client, struct wl_resource *resource)
>>               return;
>>       }
>>
>> +     if (!offer->source->current_dnd_action ||
>> +         offer->source->current_dnd_action ==
>> +         WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) {
>> +             wl_resource_post_error(offer->resource,
>> +                                    WL_DATA_OFFER_ERROR_INVALID_OFFER,
>> +                                    "offer finished with an invalid action");
>> +             return;
>> +     }
>> +
>>       data_offer_notify_finish(offer);
>>  }
>>
>> @@ -134,6 +230,7 @@ static const struct wl_data_offer_interface data_offer_interface = {
>>       data_offer_receive,
>>       data_offer_destroy,
>>       data_offer_finish,
>> +     data_offer_set_actions,
>>  };
>>
>>  static void
>> @@ -183,7 +280,8 @@ weston_data_source_send_offer(struct weston_data_source *source,
>>
>>       offer->resource =
>>               wl_resource_create(wl_resource_get_client(target),
>> -                                &wl_data_offer_interface, 1, 0);
>> +                                &wl_data_offer_interface,
>> +                                wl_resource_get_version(source->resource), 0);
>>       if (offer->resource == NULL) {
>>               free(offer);
>>               return NULL;
>> @@ -192,6 +290,8 @@ weston_data_source_send_offer(struct weston_data_source *source,
>>       wl_resource_set_implementation(offer->resource, &data_offer_interface,
>>                                      offer, destroy_data_offer);
>>
>> +     offer->dnd_actions = 0;
>> +     offer->preferred_dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
>>       offer->source = source;
>>       offer->source_destroy_listener.notify = destroy_offer_data_source;
>>       wl_signal_add(&source->destroy_signal,
>> @@ -204,6 +304,7 @@ weston_data_source_send_offer(struct weston_data_source *source,
>>
>>       source->offer = offer;
>>       source->accepted = false;
>> +     data_offer_update_action(offer);
>>
>>       return offer->resource;
>>  }
>> @@ -230,9 +331,53 @@ data_source_destroy(struct wl_client *client, struct wl_resource *resource)
>>       wl_resource_destroy(resource);
>>  }
>>
>> +static void
>> +data_source_set_actions(struct wl_client *client,
>> +                     struct wl_resource *resource,
>> +                     uint32_t dnd_actions)
>> +{
>> +     struct weston_data_source *source =
>> +             wl_resource_get_user_data(resource);
>> +
>> +     if (source->actions_set) {
>> +             wl_resource_post_error(source->resource,
>> +                                    WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK,
>> +                                    "cannot set actions more than once");
>> +             return;
>> +     }
>> +
>> +     if (dnd_actions & ~ALL_ACTIONS) {
>> +             wl_resource_post_error(source->resource,
>> +                                    WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK,
>> +                                    "invalid action mask %x", dnd_actions);
>> +             return;
>> +     }
>> +
>> +     if (!source->seat) {
>> +             wl_resource_post_error(source->resource,
>> +                                    WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK,
>> +                                    "invalid action change after "
>> +                                    "wl_data_source.dnd_drop_performed");
>> +             return;
>> +     }
>
> Shouldn't this be if (source->seat) { error() } ? In the most recent
> protocol version the requirement is that .set_actions() must be called
> before the .start_drag() request, and it's not until .start_drag()
> source->seat is set.

Doh my bad. This means the client needs changing, because it still worked.

>
>> +
>> +     source->dnd_actions = dnd_actions;
>> +     source->actions_set = true;
>> +
>> +     if (source->offer) {
>> +             if (wl_resource_get_version(source->offer->resource) >=
>> +                 WL_DATA_OFFER_SOURCE_ACTIONS_SINCE_VERSION) {
>> +                     wl_data_offer_send_source_actions(source->offer->resource,
>> +                                                       dnd_actions);
>> +             }
>> +             data_offer_update_action(source->offer);
>> +     }
>
> Should this be dropped now? We haven't started the drag, so I guess no
> offer could possibly exist yet.

Ah yes, indeed.

Cheers,
  Carlos


More information about the wayland-devel mailing list