2017-06-30 11:59:21 -07:00
|
|
|
/*
|
|
|
|
* 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;
|
2018-04-06 07:38:10 -07:00
|
|
|
struct wl_resource *res;
|
2017-06-30 11:59:21 -07:00
|
|
|
|
|
|
|
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)
|
2018-04-06 07:38:10 -07:00
|
|
|
{
|
|
|
|
res = data_device_find(s, s->drag.enter->res);
|
|
|
|
if (!res) return;
|
2017-06-30 11:59:21 -07:00
|
|
|
|
2018-04-06 07:38:10 -07:00
|
|
|
wl_data_device_send_drop(res);
|
|
|
|
}
|
2017-06-30 11:59:21 -07:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
2018-01-24 13:16:17 -08:00
|
|
|
|
|
|
|
#ifdef HAVE_ECORE_X
|
|
|
|
static xkb_mod_index_t x11_kbd_shift_mod;
|
|
|
|
static xkb_mod_index_t x11_kbd_caps_mod;
|
|
|
|
static xkb_mod_index_t x11_kbd_ctrl_mod;
|
|
|
|
static xkb_mod_index_t x11_kbd_alt_mod;
|
|
|
|
static xkb_mod_index_t x11_kbd_mod2_mod;
|
|
|
|
static xkb_mod_index_t x11_kbd_mod3_mod;
|
|
|
|
static xkb_mod_index_t x11_kbd_super_mod;
|
|
|
|
static xkb_mod_index_t x11_kbd_mod5_mod;
|
|
|
|
|
|
|
|
static void
|
|
|
|
keymap_mods_init(struct xkb_keymap *keymap)
|
|
|
|
{
|
|
|
|
x11_kbd_shift_mod = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_SHIFT);
|
|
|
|
x11_kbd_caps_mod = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_CAPS);
|
|
|
|
x11_kbd_ctrl_mod = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_CTRL);
|
|
|
|
x11_kbd_alt_mod = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_ALT);
|
|
|
|
x11_kbd_mod2_mod = xkb_keymap_mod_get_index(keymap, "Mod2");
|
|
|
|
x11_kbd_mod3_mod = xkb_keymap_mod_get_index(keymap, "Mod3");
|
|
|
|
x11_kbd_super_mod = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_LOGO);
|
|
|
|
x11_kbd_mod5_mod = xkb_keymap_mod_get_index(keymap, "Mod5");
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t
|
|
|
|
get_xkb_mod_mask(uint32_t in)
|
|
|
|
{
|
|
|
|
uint32_t ret = 0;
|
|
|
|
|
|
|
|
if ((in & ECORE_X_MODIFIER_SHIFT) && x11_kbd_shift_mod != XKB_MOD_INVALID)
|
|
|
|
ret |= (1 << x11_kbd_shift_mod);
|
|
|
|
if ((in & ECORE_X_LOCK_CAPS) && x11_kbd_caps_mod != XKB_MOD_INVALID)
|
|
|
|
ret |= (1 << x11_kbd_caps_mod);
|
|
|
|
if ((in & ECORE_X_MODIFIER_CTRL) && x11_kbd_ctrl_mod != XKB_MOD_INVALID)
|
|
|
|
ret |= (1 << x11_kbd_ctrl_mod);
|
|
|
|
if ((in & ECORE_X_MODIFIER_ALT) && x11_kbd_alt_mod != XKB_MOD_INVALID)
|
|
|
|
ret |= (1 << x11_kbd_alt_mod);
|
|
|
|
if ((in & ECORE_X_LOCK_NUM) && x11_kbd_mod2_mod != XKB_MOD_INVALID)
|
|
|
|
ret |= (1 << x11_kbd_mod2_mod);
|
|
|
|
if ((in & ECORE_X_LOCK_SCROLL) && x11_kbd_mod3_mod != XKB_MOD_INVALID)
|
|
|
|
ret |= (1 << x11_kbd_mod3_mod);
|
|
|
|
if ((in & ECORE_X_MODIFIER_WIN) && x11_kbd_super_mod != XKB_MOD_INVALID)
|
|
|
|
ret |= (1 << x11_kbd_super_mod);
|
|
|
|
if ((in & ECORE_X_MODIFIER_ALTGR) && x11_kbd_mod5_mod != XKB_MOD_INVALID)
|
|
|
|
ret |= (1 << x11_kbd_mod5_mod);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|