mirror of
https://gitlab.freedesktop.org/wlroots/wlroots.git
synced 2026-04-03 07:17:06 -04:00
xwayland: parse DND_ENTER messages
This commit is contained in:
parent
b547279625
commit
7d18812735
3 changed files with 223 additions and 86 deletions
|
|
@ -65,6 +65,8 @@ bool primary_selection_source_is_xwayland(
|
||||||
struct wlr_primary_selection_source *wlr_source);
|
struct wlr_primary_selection_source *wlr_source);
|
||||||
|
|
||||||
void xwm_seat_handle_start_drag(struct wlr_xwm *xwm, struct wlr_drag *drag);
|
void xwm_seat_handle_start_drag(struct wlr_xwm *xwm, struct wlr_drag *drag);
|
||||||
|
int xwm_dnd_handle_xfixes_selection_notify(struct wlr_xwm *xwm,
|
||||||
|
xcb_xfixes_selection_notify_event_t *event);
|
||||||
|
|
||||||
void xwm_selection_init(struct wlr_xwm *xwm);
|
void xwm_selection_init(struct wlr_xwm *xwm);
|
||||||
void xwm_selection_finish(struct wlr_xwm *xwm);
|
void xwm_selection_finish(struct wlr_xwm *xwm);
|
||||||
|
|
|
||||||
|
|
@ -163,76 +163,207 @@ static void xwm_dnd_send_leave(struct wlr_xwm *xwm) {
|
||||||
xwm_dnd_send_event(xwm, xwm->atoms[DND_FINISHED], &data);
|
xwm_dnd_send_event(xwm, xwm->atoms[DND_FINISHED], &data);
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle a status message for an outgoing DnD operation.
|
||||||
|
*/
|
||||||
|
static void xwm_handle_dnd_status(struct wlr_xwm *xwm,
|
||||||
|
xcb_client_message_data_t *data) {
|
||||||
|
if (xwm->drag == NULL) {
|
||||||
|
wlr_log(L_DEBUG, "ignoring XdndStatus client message because "
|
||||||
|
"there's no drag");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
xcb_window_t target_window = data->data32[0];
|
||||||
|
bool accepted = data->data32[1] & 1;
|
||||||
|
xcb_atom_t action_atom = data->data32[4];
|
||||||
|
|
||||||
|
if (xwm->drag_focus == NULL ||
|
||||||
|
target_window != xwm->drag_focus->window_id) {
|
||||||
|
wlr_log(L_DEBUG, "ignoring XdndStatus client message because "
|
||||||
|
"it doesn't match the current drag focus window ID");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum wl_data_device_manager_dnd_action action =
|
||||||
|
data_device_manager_dnd_action_from_atom(xwm, action_atom);
|
||||||
|
|
||||||
|
struct wlr_drag *drag = xwm->drag;
|
||||||
|
assert(drag != NULL);
|
||||||
|
|
||||||
|
drag->source->accepted = accepted;
|
||||||
|
wlr_data_source_dnd_action(drag->source, action);
|
||||||
|
|
||||||
|
wlr_log(L_DEBUG, "DND_STATUS window=%d accepted=%d action=%d",
|
||||||
|
target_window, accepted, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle a finish message for an outgoing DnD operation.
|
||||||
|
*/
|
||||||
|
static void xwm_handle_dnd_finished(struct wlr_xwm *xwm,
|
||||||
|
xcb_client_message_data_t *data) {
|
||||||
|
// This should only happen after the drag has ended, but before the drag
|
||||||
|
// source is destroyed
|
||||||
|
if (xwm->seat == NULL || xwm->seat->drag_source == NULL ||
|
||||||
|
xwm->drag != NULL) {
|
||||||
|
wlr_log(L_DEBUG, "ignoring XdndFinished client message because "
|
||||||
|
"there's no finished drag");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_data_source *source = xwm->seat->drag_source;
|
||||||
|
|
||||||
|
xcb_window_t target_window = data->data32[0];
|
||||||
|
bool performed = data->data32[1] & 1;
|
||||||
|
xcb_atom_t action_atom = data->data32[2];
|
||||||
|
|
||||||
|
if (xwm->drag_focus == NULL ||
|
||||||
|
target_window != xwm->drag_focus->window_id) {
|
||||||
|
wlr_log(L_DEBUG, "ignoring XdndFinished client message because "
|
||||||
|
"it doesn't match the finished drag focus window ID");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum wl_data_device_manager_dnd_action action =
|
||||||
|
data_device_manager_dnd_action_from_atom(xwm, action_atom);
|
||||||
|
|
||||||
|
if (performed) {
|
||||||
|
wlr_data_source_dnd_finish(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
wlr_log(L_DEBUG, "DND_FINISH window=%d performed=%d action=%d",
|
||||||
|
target_window, performed, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool xwm_add_atom_to_mime_types(struct wlr_xwm *xwm,
|
||||||
|
struct wl_array *mime_types, xcb_atom_t atom) {
|
||||||
|
char *mime_type = xwm_mime_type_from_atom(xwm, atom);
|
||||||
|
if (mime_type == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
char **mime_type_ptr =
|
||||||
|
wl_array_add(mime_types, sizeof(*mime_type_ptr));
|
||||||
|
if (mime_type_ptr == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*mime_type_ptr = mime_type;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool xwm_dnd_get_mime_types(struct wlr_xwm *xwm,
|
||||||
|
struct wl_array *mime_types, xcb_window_t source) {
|
||||||
|
xcb_get_property_cookie_t cookie = xcb_get_property(xwm->xcb_conn,
|
||||||
|
1, // delete
|
||||||
|
source,
|
||||||
|
xwm->atoms[DND_TYPE_LIST],
|
||||||
|
XCB_GET_PROPERTY_TYPE_ANY,
|
||||||
|
0, // offset
|
||||||
|
0x1fffffff // length
|
||||||
|
);
|
||||||
|
|
||||||
|
xcb_get_property_reply_t *reply =
|
||||||
|
xcb_get_property_reply(xwm->xcb_conn, cookie, NULL);
|
||||||
|
if (reply == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (reply->type != XCB_ATOM_ATOM || reply->value_len == 0) {
|
||||||
|
wlr_log(L_ERROR, "invalid XdndTypeList property");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
xcb_atom_t *atoms = xcb_get_property_value(reply);
|
||||||
|
for (uint32_t i = 0; i < reply->value_len; ++i) {
|
||||||
|
if (!xwm_add_atom_to_mime_types(xwm, mime_types, atoms[i])) {
|
||||||
|
wlr_log(L_ERROR, "failed to add MIME type atom to list");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(reply);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
error:
|
||||||
|
free(reply);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle an enter message for an incoming DnD operation.
|
||||||
|
*/
|
||||||
|
static void xwm_handle_dnd_enter(struct wlr_xwm *xwm,
|
||||||
|
xcb_client_message_data_t *data) {
|
||||||
|
if (xwm->seat == NULL) {
|
||||||
|
wlr_log(L_DEBUG, "ignoring XdndEnter client message because "
|
||||||
|
"there's no Xwayland seat");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
xcb_window_t source_window = data->data32[0];
|
||||||
|
|
||||||
|
if (source_window != xwm->dnd_selection.owner) {
|
||||||
|
wlr_log(L_DEBUG, "ignoring XdndEnter client message because "
|
||||||
|
"the source window hasn't set the drag-and-drop selection");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wl_array mime_types;
|
||||||
|
wl_array_init(&mime_types);
|
||||||
|
if ((data->data32[1] & 1) == 0) {
|
||||||
|
// Less than 3 MIME types, those are in the message data
|
||||||
|
for (size_t i = 0; i < 3; ++i) {
|
||||||
|
xcb_atom_t atom = data->data32[2+i];
|
||||||
|
if (atom == XCB_ATOM_NONE) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!xwm_add_atom_to_mime_types(xwm, &mime_types, atom)) {
|
||||||
|
wlr_log(L_ERROR, "failed to add MIME type atom to list");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!xwm_dnd_get_mime_types(xwm, &mime_types, source_window)) {
|
||||||
|
wlr_log(L_ERROR, "failed to add MIME type atom to list");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wlr_log(L_INFO, "parsed XdndEnter");
|
||||||
|
}
|
||||||
|
|
||||||
int xwm_handle_selection_client_message(struct wlr_xwm *xwm,
|
int xwm_handle_selection_client_message(struct wlr_xwm *xwm,
|
||||||
xcb_client_message_event_t *ev) {
|
xcb_client_message_event_t *ev) {
|
||||||
|
// Outgoing
|
||||||
if (ev->type == xwm->atoms[DND_STATUS]) {
|
if (ev->type == xwm->atoms[DND_STATUS]) {
|
||||||
if (xwm->drag == NULL) {
|
xwm_handle_dnd_status(xwm, &ev->data);
|
||||||
wlr_log(L_DEBUG, "ignoring XdndStatus client message because "
|
|
||||||
"there's no drag");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
xcb_client_message_data_t *data = &ev->data;
|
|
||||||
xcb_window_t target_window = data->data32[0];
|
|
||||||
bool accepted = data->data32[1] & 1;
|
|
||||||
xcb_atom_t action_atom = data->data32[4];
|
|
||||||
|
|
||||||
if (xwm->drag_focus == NULL ||
|
|
||||||
target_window != xwm->drag_focus->window_id) {
|
|
||||||
wlr_log(L_DEBUG, "ignoring XdndStatus client message because "
|
|
||||||
"it doesn't match the current drag focus window ID");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum wl_data_device_manager_dnd_action action =
|
|
||||||
data_device_manager_dnd_action_from_atom(xwm, action_atom);
|
|
||||||
|
|
||||||
struct wlr_drag *drag = xwm->drag;
|
|
||||||
assert(drag != NULL);
|
|
||||||
|
|
||||||
drag->source->accepted = accepted;
|
|
||||||
wlr_data_source_dnd_action(drag->source, action);
|
|
||||||
|
|
||||||
wlr_log(L_DEBUG, "DND_STATUS window=%d accepted=%d action=%d",
|
|
||||||
target_window, accepted, action);
|
|
||||||
return 1;
|
return 1;
|
||||||
} else if (ev->type == xwm->atoms[DND_FINISHED]) {
|
} else if (ev->type == xwm->atoms[DND_FINISHED]) {
|
||||||
// This should only happen after the drag has ended, but before the drag
|
xwm_handle_dnd_finished(xwm, &ev->data);
|
||||||
// source is destroyed
|
|
||||||
if (xwm->seat == NULL || xwm->seat->drag_source == NULL ||
|
|
||||||
xwm->drag != NULL) {
|
|
||||||
wlr_log(L_DEBUG, "ignoring XdndFinished client message because "
|
|
||||||
"there's no finished drag");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_data_source *source = xwm->seat->drag_source;
|
|
||||||
|
|
||||||
xcb_client_message_data_t *data = &ev->data;
|
|
||||||
xcb_window_t target_window = data->data32[0];
|
|
||||||
bool performed = data->data32[1] & 1;
|
|
||||||
xcb_atom_t action_atom = data->data32[2];
|
|
||||||
|
|
||||||
if (xwm->drag_focus == NULL ||
|
|
||||||
target_window != xwm->drag_focus->window_id) {
|
|
||||||
wlr_log(L_DEBUG, "ignoring XdndFinished client message because "
|
|
||||||
"it doesn't match the finished drag focus window ID");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum wl_data_device_manager_dnd_action action =
|
|
||||||
data_device_manager_dnd_action_from_atom(xwm, action_atom);
|
|
||||||
|
|
||||||
if (performed) {
|
|
||||||
wlr_data_source_dnd_finish(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_log(L_DEBUG, "DND_FINISH window=%d performed=%d action=%d",
|
|
||||||
target_window, performed, action);
|
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Incoming
|
||||||
|
if (ev->type == xwm->atoms[DND_ENTER]) {
|
||||||
|
xwm_handle_dnd_enter(xwm, &ev->data);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int xwm_dnd_handle_xfixes_selection_notify(struct wlr_xwm *xwm,
|
||||||
|
xcb_xfixes_selection_notify_event_t *event) {
|
||||||
|
assert(event->selection == xwm->atoms[DND_SELECTION]);
|
||||||
|
struct wlr_xwm_selection *selection = &xwm->dnd_selection;
|
||||||
|
|
||||||
|
selection->owner = event->owner;
|
||||||
|
|
||||||
|
if (event->owner != XCB_ATOM_NONE) {
|
||||||
|
// TODO: start grab
|
||||||
|
} else {
|
||||||
|
// TODO: end grab
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void seat_handle_drag_focus(struct wl_listener *listener, void *data) {
|
static void seat_handle_drag_focus(struct wl_listener *listener, void *data) {
|
||||||
|
|
|
||||||
|
|
@ -379,8 +379,7 @@ static void xwm_selection_get_targets(struct wlr_xwm_selection *selection) {
|
||||||
void xwm_handle_selection_notify(struct wlr_xwm *xwm,
|
void xwm_handle_selection_notify(struct wlr_xwm *xwm,
|
||||||
xcb_selection_notify_event_t *event) {
|
xcb_selection_notify_event_t *event) {
|
||||||
wlr_log(L_DEBUG, "XCB_SELECTION_NOTIFY (selection=%u, property=%u, target=%u)",
|
wlr_log(L_DEBUG, "XCB_SELECTION_NOTIFY (selection=%u, property=%u, target=%u)",
|
||||||
event->selection, event->property,
|
event->selection, event->property, event->target);
|
||||||
event->target);
|
|
||||||
|
|
||||||
struct wlr_xwm_selection *selection =
|
struct wlr_xwm_selection *selection =
|
||||||
xwm_get_selection(xwm, event->selection);
|
xwm_get_selection(xwm, event->selection);
|
||||||
|
|
@ -410,6 +409,11 @@ int xwm_handle_xfixes_selection_notify(struct wlr_xwm *xwm,
|
||||||
wlr_log(L_DEBUG, "XCB_XFIXES_SELECTION_NOTIFY (selection=%u, owner=%u)",
|
wlr_log(L_DEBUG, "XCB_XFIXES_SELECTION_NOTIFY (selection=%u, owner=%u)",
|
||||||
event->selection, event->owner);
|
event->selection, event->owner);
|
||||||
|
|
||||||
|
// Drag'n'drop works differently
|
||||||
|
if (event->selection == xwm->atoms[DND_SELECTION]) {
|
||||||
|
return xwm_dnd_handle_xfixes_selection_notify(xwm, event);
|
||||||
|
}
|
||||||
|
|
||||||
struct wlr_xwm_selection *selection =
|
struct wlr_xwm_selection *selection =
|
||||||
xwm_get_selection(xwm, event->selection);
|
xwm_get_selection(xwm, event->selection);
|
||||||
if (selection == NULL) {
|
if (selection == NULL) {
|
||||||
|
|
@ -435,28 +439,28 @@ int xwm_handle_xfixes_selection_notify(struct wlr_xwm *xwm,
|
||||||
}
|
}
|
||||||
|
|
||||||
selection->owner = XCB_WINDOW_NONE;
|
selection->owner = XCB_WINDOW_NONE;
|
||||||
return 1;
|
} else {
|
||||||
|
selection->owner = event->owner;
|
||||||
|
|
||||||
|
// This is our selection window.
|
||||||
|
// We have to use XCB_TIME_CURRENT_TIME when we claim the
|
||||||
|
// selection, so grab the actual timestamp here so we can
|
||||||
|
// answer TIMESTAMP conversion requests correctly.
|
||||||
|
if (event->owner == selection->window) {
|
||||||
|
selection->timestamp = event->timestamp;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_xwm_selection_transfer *transfer = &selection->incoming;
|
||||||
|
transfer->incr = false;
|
||||||
|
// doing this will give a selection notify where we actually handle the sync
|
||||||
|
xcb_convert_selection(xwm->xcb_conn, selection->window,
|
||||||
|
selection->atom,
|
||||||
|
xwm->atoms[TARGETS],
|
||||||
|
xwm->atoms[WL_SELECTION],
|
||||||
|
event->timestamp);
|
||||||
|
xcb_flush(xwm->xcb_conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
selection->owner = event->owner;
|
|
||||||
|
|
||||||
// We have to use XCB_TIME_CURRENT_TIME when we claim the
|
|
||||||
// selection, so grab the actual timestamp here so we can
|
|
||||||
// answer TIMESTAMP conversion requests correctly.
|
|
||||||
if (event->owner == selection->window) {
|
|
||||||
selection->timestamp = event->timestamp;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_xwm_selection_transfer *transfer = &selection->incoming;
|
|
||||||
transfer->incr = false;
|
|
||||||
// doing this will give a selection notify where we actually handle the sync
|
|
||||||
xcb_convert_selection(xwm->xcb_conn, selection->window,
|
|
||||||
selection->atom,
|
|
||||||
xwm->atoms[TARGETS],
|
|
||||||
xwm->atoms[WL_SELECTION],
|
|
||||||
event->timestamp);
|
|
||||||
xcb_flush(xwm->xcb_conn);
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue