xwayland: send DND_ENTER

This commit is contained in:
emersion 2018-03-28 12:59:11 -04:00
parent b6c1760de5
commit 0d7a81ccdf
No known key found for this signature in database
GPG key ID: 0FDE7BE0E88F5E48
7 changed files with 192 additions and 82 deletions

View file

@ -104,6 +104,11 @@ struct wlr_drag {
struct wl_listener source_destroy; struct wl_listener source_destroy;
struct wl_listener seat_client_destroy; struct wl_listener seat_client_destroy;
struct wl_listener icon_destroy; struct wl_listener icon_destroy;
struct {
struct wl_signal focus;
struct wl_signal destroy;
} events;
}; };
/** /**

View file

@ -187,6 +187,9 @@ struct wlr_seat {
struct wlr_primary_selection_source *primary_selection_source; struct wlr_primary_selection_source *primary_selection_source;
uint32_t primary_selection_serial; uint32_t primary_selection_serial;
struct wlr_drag *drag;
uint32_t drag_serial;
struct wlr_seat_pointer_state pointer_state; struct wlr_seat_pointer_state pointer_state;
struct wlr_seat_keyboard_state keyboard_state; struct wlr_seat_keyboard_state keyboard_state;
struct wlr_seat_touch_state touch_state; struct wlr_seat_touch_state touch_state;
@ -210,6 +213,7 @@ struct wlr_seat {
struct wl_signal selection; struct wl_signal selection;
struct wl_signal primary_selection; struct wl_signal primary_selection;
struct wl_signal start_drag;
struct wl_signal new_drag_icon; struct wl_signal new_drag_icon;
struct wl_signal destroy; struct wl_signal destroy;

View file

@ -115,8 +115,7 @@ struct wlr_xwm {
xcb_window_t selection_window; xcb_window_t selection_window;
struct wlr_xwm_selection clipboard_selection; struct wlr_xwm_selection clipboard_selection;
struct wlr_xwm_selection primary_selection; struct wlr_xwm_selection primary_selection;
struct wlr_xwm_selection dnd_selection;
xcb_window_t dnd_window;
struct wlr_xwayland_surface *focus_surface; struct wlr_xwayland_surface *focus_surface;
@ -132,6 +131,9 @@ struct wlr_xwm {
struct wl_listener compositor_destroy; struct wl_listener compositor_destroy;
struct wl_listener seat_selection; struct wl_listener seat_selection;
struct wl_listener seat_primary_selection; struct wl_listener seat_primary_selection;
struct wl_listener seat_start_drag;
struct wl_listener seat_drag_focus;
struct wl_listener seat_drag_destroy;
}; };
struct wlr_xwm *xwm_create(struct wlr_xwayland *wlr_xwayland); struct wlr_xwm *xwm_create(struct wlr_xwayland *wlr_xwayland);

View file

@ -131,7 +131,8 @@ static void roots_cursor_update_position(struct roots_cursor *cursor,
cursor->pointer_view = seat_view; cursor->pointer_view = seat_view;
seat_view_deco_motion(seat_view, sx, sy); seat_view_deco_motion(seat_view, sx, sy);
} }
} if (view && surface) { }
if (view && surface) {
// motion over a view surface // motion over a view surface
wlr_seat_pointer_notify_enter(seat->seat, surface, sx, sy); wlr_seat_pointer_notify_enter(seat->seat, surface, sx, sy);
wlr_seat_pointer_notify_motion(seat->seat, time, sx, sy); wlr_seat_pointer_notify_motion(seat->seat, time, sx, sy);

View file

@ -427,7 +427,7 @@ static void wlr_drag_set_focus(struct wlr_drag *drag,
wlr_seat_client_for_wl_client(drag->seat_client->seat, wlr_seat_client_for_wl_client(drag->seat_client->seat,
wl_resource_get_client(surface->resource)); wl_resource_get_client(surface->resource));
if (!focus_client || wl_list_empty(&focus_client->data_devices)) { if (!focus_client) {
return; return;
} }
@ -436,34 +436,36 @@ static void wlr_drag_set_focus(struct wlr_drag *drag,
drag->source->accepted = false; drag->source->accepted = false;
struct wlr_data_offer *offer = wlr_data_source_send_offer(drag->source, struct wlr_data_offer *offer = wlr_data_source_send_offer(drag->source,
focus_client); focus_client);
if (offer == NULL) { if (offer != NULL) {
return; data_offer_update_action(offer);
if (wl_resource_get_version(offer->resource) >=
WL_DATA_OFFER_SOURCE_ACTIONS_SINCE_VERSION) {
wl_data_offer_send_source_actions(offer->resource,
drag->source->actions);
}
offer_resource = offer->resource;
} }
data_offer_update_action(offer);
if (wl_resource_get_version(offer->resource) >=
WL_DATA_OFFER_SOURCE_ACTIONS_SINCE_VERSION) {
wl_data_offer_send_source_actions(offer->resource,
drag->source->actions);
}
offer_resource = offer->resource;
} }
uint32_t serial = if (!wl_list_empty(&focus_client->data_devices)) {
wl_display_next_serial(drag->seat_client->seat->display); uint32_t serial =
struct wl_resource *resource; wl_display_next_serial(drag->seat_client->seat->display);
wl_resource_for_each(resource, &focus_client->data_devices) { struct wl_resource *resource;
wl_data_device_send_enter(resource, serial, surface->resource, wl_resource_for_each(resource, &focus_client->data_devices) {
wl_fixed_from_double(sx), wl_fixed_from_double(sy), offer_resource); wl_data_device_send_enter(resource, serial, surface->resource,
wl_fixed_from_double(sx), wl_fixed_from_double(sy),
offer_resource);
}
} }
drag->focus = surface; drag->focus = surface;
drag->focus_client = focus_client; drag->focus_client = focus_client;
drag->seat_client_destroy.notify = handle_drag_seat_client_destroy; drag->seat_client_destroy.notify = handle_drag_seat_client_destroy;
wl_signal_add(&focus_client->events.destroy, wl_signal_add(&focus_client->events.destroy, &drag->seat_client_destroy);
&drag->seat_client_destroy);
wlr_signal_emit_safe(&drag->events.focus, drag);
} }
static void wlr_drag_end(struct wlr_drag *drag) { static void wlr_drag_end(struct wlr_drag *drag) {
@ -488,10 +490,14 @@ static void wlr_drag_end(struct wlr_drag *drag) {
wlr_signal_emit_safe(&drag->icon->events.map, drag->icon); wlr_signal_emit_safe(&drag->icon->events.map, drag->icon);
} }
wlr_signal_emit_safe(&drag->events.destroy, drag);
free(drag); free(drag);
} }
} }
const struct
wlr_pointer_grab_interface wlr_data_device_pointer_drag_interface;
static void pointer_drag_enter(struct wlr_seat_pointer_grab *grab, static void pointer_drag_enter(struct wlr_seat_pointer_grab *grab,
struct wlr_surface *surface, double sx, double sy) { struct wlr_surface *surface, double sx, double sy) {
struct wlr_drag *drag = grab->data; struct wlr_drag *drag = grab->data;
@ -732,22 +738,26 @@ static bool seat_client_start_drag(struct wlr_seat_client *client,
return false; return false;
} }
drag->seat = client->seat; wl_signal_init(&drag->events.focus);
wl_signal_init(&drag->events.destroy);
struct wlr_seat *seat = client->seat;
drag->seat = seat;
drag->is_pointer_grab = !wl_list_empty(&client->pointers) && drag->is_pointer_grab = !wl_list_empty(&client->pointers) &&
client->seat->pointer_state.button_count == 1 && seat->pointer_state.button_count == 1 &&
client->seat->pointer_state.grab_serial == serial && seat->pointer_state.grab_serial == serial &&
client->seat->pointer_state.focused_surface && seat->pointer_state.focused_surface &&
client->seat->pointer_state.focused_surface == origin; seat->pointer_state.focused_surface == origin;
bool is_touch_grab = !wl_list_empty(&client->touches) && bool is_touch_grab = !wl_list_empty(&client->touches) &&
wlr_seat_touch_num_points(client->seat) == 1 && wlr_seat_touch_num_points(seat) == 1 &&
client->seat->touch_state.grab_serial == serial; seat->touch_state.grab_serial == serial;
// set in the iteration // set in the iteration
struct wlr_touch_point *point = NULL; struct wlr_touch_point *point = NULL;
if (is_touch_grab) { if (is_touch_grab) {
wl_list_for_each(point, &client->seat->touch_state.touch_points, link) { wl_list_for_each(point, &seat->touch_state.touch_points, link) {
is_touch_grab = point->surface && point->surface == origin; is_touch_grab = point->surface && point->surface == origin;
break; break;
} }
@ -785,22 +795,26 @@ static bool seat_client_start_drag(struct wlr_seat_client *client,
drag->touch_grab.data = drag; drag->touch_grab.data = drag;
drag->touch_grab.interface = &wlr_data_device_touch_drag_interface; drag->touch_grab.interface = &wlr_data_device_touch_drag_interface;
drag->grab_touch_id = drag->seat->touch_state.grab_id; drag->grab_touch_id = seat->touch_state.grab_id;
drag->keyboard_grab.data = drag; drag->keyboard_grab.data = drag;
drag->keyboard_grab.interface = &wlr_data_device_keyboard_drag_interface; drag->keyboard_grab.interface = &wlr_data_device_keyboard_drag_interface;
wlr_seat_keyboard_start_grab(drag->seat, &drag->keyboard_grab); wlr_seat_keyboard_start_grab(seat, &drag->keyboard_grab);
if (drag->is_pointer_grab) { if (drag->is_pointer_grab) {
wlr_seat_pointer_clear_focus(drag->seat); wlr_seat_pointer_clear_focus(seat);
wlr_seat_pointer_start_grab(drag->seat, &drag->pointer_grab); wlr_seat_pointer_start_grab(seat, &drag->pointer_grab);
} else { } else {
assert(point); assert(point);
wlr_seat_touch_start_grab(drag->seat, &drag->touch_grab); wlr_seat_touch_start_grab(seat, &drag->touch_grab);
wlr_drag_set_focus(drag, point->surface, point->sx, point->sy); wlr_drag_set_focus(drag, point->surface, point->sx, point->sy);
} }
seat->drag = drag; // TODO: unset this thing somewhere
seat->drag_serial = serial;
wlr_signal_emit_safe(&seat->events.start_drag, drag);
return true; return true;
} }

View file

@ -465,6 +465,7 @@ struct wlr_seat *wlr_seat_create(struct wl_display *display, const char *name) {
wl_list_init(&wlr_seat->clients); wl_list_init(&wlr_seat->clients);
wl_list_init(&wlr_seat->drag_icons); wl_list_init(&wlr_seat->drag_icons);
wl_signal_init(&wlr_seat->events.start_drag);
wl_signal_init(&wlr_seat->events.new_drag_icon); wl_signal_init(&wlr_seat->events.new_drag_icon);
wl_signal_init(&wlr_seat->events.request_set_cursor); wl_signal_init(&wlr_seat->events.request_set_cursor);

View file

@ -12,7 +12,7 @@
static const size_t incr_chunk_size = 64 * 1024; static const size_t incr_chunk_size = 64 * 1024;
static xcb_atom_t data_device_manager_dnd_action_to_atom( /*static xcb_atom_t data_device_manager_dnd_action_to_atom(
struct wlr_xwm *xwm, enum wl_data_device_manager_dnd_action action) { struct wlr_xwm *xwm, enum wl_data_device_manager_dnd_action action) {
if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) { if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) {
return xwm->atoms[DND_ACTION_COPY]; return xwm->atoms[DND_ACTION_COPY];
@ -36,7 +36,7 @@ static enum wl_data_device_manager_dnd_action
return WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK; return WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK;
} }
return WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; return WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
} }*/
static void xwm_selection_send_notify(struct wlr_xwm_selection *selection, static void xwm_selection_send_notify(struct wlr_xwm_selection *selection,
xcb_atom_t property) { xcb_atom_t property) {
@ -241,6 +241,81 @@ static void xwm_selection_send_timestamp(struct wlr_xwm_selection *selection) {
xwm_selection_send_notify(selection, selection->request.property); xwm_selection_send_notify(selection, selection->request.property);
} }
static xcb_atom_t xwm_get_mime_type_atom(struct wlr_xwm *xwm, char *mime_type) {
if (strcmp(mime_type, "text/plain;charset=utf-8") == 0) {
return xwm->atoms[UTF8_STRING];
} else if (strcmp(mime_type, "text/plain") == 0) {
return xwm->atoms[TEXT];
}
xcb_intern_atom_cookie_t cookie =
xcb_intern_atom(xwm->xcb_conn, 0, strlen(mime_type), mime_type);
xcb_intern_atom_reply_t *reply =
xcb_intern_atom_reply(xwm->xcb_conn, cookie, NULL);
if (reply == NULL) {
return XCB_ATOM_NONE;
}
xcb_atom_t atom = reply->atom;
free(reply);
return atom;
}
static void xwm_dnd_send_enter(struct wlr_xwm *xwm, struct wlr_drag *drag,
struct wlr_xwayland_surface *dest) {
struct wl_array *mime_types = &drag->source->mime_types;
xcb_client_message_data_t data = { 0 };
data.data32[0] = xwm->dnd_selection.window;
data.data32[1] = XDND_VERSION << 24;
size_t n = mime_types->size / sizeof(char *);
if (n <= 3) {
size_t i = 0;
char **mime_type_ptr;
wl_array_for_each(mime_type_ptr, mime_types) {
char *mime_type = *mime_type_ptr;
data.data32[2+i] = xwm_get_mime_type_atom(xwm, mime_type);
++i;
}
} else {
// Let the client know that targets are not contained in the message
// data and must be retrieved with the DND_TYPE_LIST property
data.data32[1] |= 1;
xcb_atom_t targets[n];
size_t i = 0;
char **mime_type_ptr;
wl_array_for_each(mime_type_ptr, mime_types) {
char *mime_type = *mime_type_ptr;
targets[i] = xwm_get_mime_type_atom(xwm, mime_type);
++i;
}
xcb_change_property(xwm->xcb_conn,
XCB_PROP_MODE_REPLACE,
xwm->dnd_selection.window,
xwm->atoms[DND_TYPE_LIST],
XCB_ATOM_ATOM,
32, // format
n, targets);
}
xcb_client_message_event_t event = {
.response_type = XCB_CLIENT_MESSAGE,
.format = 32,
.sequence = 0,
.window = dest->window_id,
.type = xwm->atoms[DND_ENTER],
.data = data,
};
xcb_send_event(xwm->xcb_conn,
0, // propagate
dest->window_id,
XCB_EVENT_MASK_NO_EVENT,
(const char *)&event);
}
static struct wl_array *xwm_selection_source_get_mime_types( static struct wl_array *xwm_selection_source_get_mime_types(
struct wlr_xwm_selection *selection) { struct wlr_xwm_selection *selection) {
if (selection == &selection->xwm->clipboard_selection) { if (selection == &selection->xwm->clipboard_selection) {
@ -271,35 +346,15 @@ static void xwm_selection_send_targets(struct wlr_xwm_selection *selection) {
} }
size_t n = 2 + mime_types->size / sizeof(char *); size_t n = 2 + mime_types->size / sizeof(char *);
xcb_atom_t *targets = malloc(n * sizeof(xcb_atom_t)); xcb_atom_t targets[n];
if (targets == NULL) {
return;
}
targets[0] = xwm->atoms[TIMESTAMP]; targets[0] = xwm->atoms[TIMESTAMP];
targets[1] = xwm->atoms[TARGETS]; targets[1] = xwm->atoms[TARGETS];
size_t i = 2; size_t i = 0;
char **mime_type_ptr; char **mime_type_ptr;
wl_array_for_each(mime_type_ptr, mime_types) { wl_array_for_each(mime_type_ptr, mime_types) {
char *mime_type = *mime_type_ptr; char *mime_type = *mime_type_ptr;
xcb_atom_t atom; targets[2+i] = xwm_get_mime_type_atom(xwm, mime_type);
if (strcmp(mime_type, "text/plain;charset=utf-8") == 0) {
atom = xwm->atoms[UTF8_STRING];
} else if (strcmp(mime_type, "text/plain") == 0) {
atom = xwm->atoms[TEXT];
} else {
xcb_intern_atom_cookie_t cookie =
xcb_intern_atom(xwm->xcb_conn, 0, strlen(mime_type), mime_type);
xcb_intern_atom_reply_t *reply =
xcb_intern_atom_reply(xwm->xcb_conn, cookie, NULL);
if (reply == NULL) {
--n;
continue;
}
atom = reply->atom;
free(reply);
}
targets[i] = atom;
++i; ++i;
} }
@ -312,8 +367,6 @@ static void xwm_selection_send_targets(struct wlr_xwm_selection *selection) {
n, targets); n, targets);
xwm_selection_send_notify(selection, selection->request.property); xwm_selection_send_notify(selection, selection->request.property);
free(targets);
} }
static struct wlr_xwm_selection *xwm_get_selection(struct wlr_xwm *xwm, static struct wlr_xwm_selection *xwm_get_selection(struct wlr_xwm *xwm,
@ -322,6 +375,8 @@ static struct wlr_xwm_selection *xwm_get_selection(struct wlr_xwm *xwm,
return &xwm->clipboard_selection; return &xwm->clipboard_selection;
} else if (selection_atom == xwm->atoms[PRIMARY]) { } else if (selection_atom == xwm->atoms[PRIMARY]) {
return &xwm->primary_selection; return &xwm->primary_selection;
} else if (selection_atom == xwm->atoms[DND_SELECTION]) {
return &xwm->dnd_selection;
} else { } else {
return NULL; return NULL;
} }
@ -856,28 +911,18 @@ void xwm_selection_init(struct wlr_xwm *xwm) {
selection_init(xwm, &xwm->primary_selection, xwm->atoms[PRIMARY]); selection_init(xwm, &xwm->primary_selection, xwm->atoms[PRIMARY]);
// Drag'n'drop // Drag'n'drop
uint32_t dnd_values[] = { XCB_EVENT_MASK_PROPERTY_CHANGE };
xwm->dnd_window = xcb_generate_id(xwm->xcb_conn);
xcb_create_window(xwm->xcb_conn,
XCB_COPY_FROM_PARENT,
xwm->dnd_window,
xwm->screen->root,
0, 0,
10, 10,
0,
XCB_WINDOW_CLASS_INPUT_OUTPUT,
xwm->screen->root_visual,
XCB_CW_EVENT_MASK, dnd_values);
uint32_t version = XDND_VERSION; uint32_t version = XDND_VERSION;
xcb_change_property(xwm->xcb_conn, xcb_change_property(xwm->xcb_conn,
XCB_PROP_MODE_REPLACE, XCB_PROP_MODE_REPLACE,
xwm->dnd_window, xwm->selection_window,
xwm->atoms[DND_AWARE], xwm->atoms[DND_AWARE],
XCB_ATOM_ATOM, XCB_ATOM_ATOM,
32, 32,
1, 1,
&version); &version);
selection_init(xwm, &xwm->dnd_selection, xwm->atoms[DND_SELECTION]);
wlr_log(L_DEBUG, "DND_SELECTION=%d", xwm->atoms[DND_SELECTION]);
} }
void xwm_selection_finish(struct wlr_xwm *xwm) { void xwm_selection_finish(struct wlr_xwm *xwm) {
@ -887,9 +932,6 @@ void xwm_selection_finish(struct wlr_xwm *xwm) {
if (xwm->selection_window) { if (xwm->selection_window) {
xcb_destroy_window(xwm->xcb_conn, xwm->selection_window); xcb_destroy_window(xwm->xcb_conn, xwm->selection_window);
} }
if (xwm->dnd_window) {
xcb_destroy_window(xwm->xcb_conn, xwm->dnd_window);
}
if (xwm->seat) { if (xwm->seat) {
if (xwm->seat->selection_data_source && if (xwm->seat->selection_data_source &&
xwm->seat->selection_data_source->cancel == data_source_cancel) { xwm->seat->selection_data_source->cancel == data_source_cancel) {
@ -903,7 +945,6 @@ void xwm_selection_finish(struct wlr_xwm *xwm) {
} }
wlr_xwayland_set_seat(xwm->xwayland, NULL); wlr_xwayland_set_seat(xwm->xwayland, NULL);
} }
} }
static void xwm_selection_set_owner(struct wlr_xwm_selection *selection, static void xwm_selection_set_owner(struct wlr_xwm_selection *selection,
@ -951,10 +992,50 @@ static void seat_handle_primary_selection(struct wl_listener *listener,
xwm_selection_set_owner(&xwm->primary_selection, source != NULL); xwm_selection_set_owner(&xwm->primary_selection, source != NULL);
} }
static void seat_handle_drag_focus(struct wl_listener *listener, void *data) {
struct wlr_drag *drag = data;
struct wlr_xwm *xwm = wl_container_of(listener, xwm, seat_drag_focus);
// TODO: check for subsurfaces?
bool found = false;
struct wlr_xwayland_surface *surface;
wl_list_for_each(surface, &xwm->surfaces, link) {
if (surface->surface == drag->focus) {
found = true;
break;
}
}
if (!found) {
return;
}
xwm_dnd_send_enter(xwm, drag, surface);
}
static void seat_handle_drag_destroy(struct wl_listener *listener, void *data) {
struct wlr_xwm *xwm = wl_container_of(listener, xwm, seat_drag_destroy);
wl_list_remove(&xwm->seat_drag_focus.link);
wl_list_remove(&xwm->seat_drag_destroy.link);
}
static void seat_handle_start_drag(struct wl_listener *listener, void *data) {
struct wlr_drag *drag = data;
struct wlr_xwm *xwm = wl_container_of(listener, xwm, seat_start_drag);
xwm_selection_set_owner(&xwm->dnd_selection, drag != NULL);
wl_signal_add(&drag->events.focus, &xwm->seat_drag_focus);
xwm->seat_drag_focus.notify = seat_handle_drag_focus;
wl_signal_add(&drag->events.destroy, &xwm->seat_drag_destroy);
xwm->seat_drag_destroy.notify = seat_handle_drag_destroy;
}
void xwm_set_seat(struct wlr_xwm *xwm, struct wlr_seat *seat) { void xwm_set_seat(struct wlr_xwm *xwm, struct wlr_seat *seat) {
if (xwm->seat != NULL) { if (xwm->seat != NULL) {
wl_list_remove(&xwm->seat_selection.link); wl_list_remove(&xwm->seat_selection.link);
wl_list_remove(&xwm->seat_primary_selection.link); wl_list_remove(&xwm->seat_primary_selection.link);
wl_list_remove(&xwm->seat_start_drag.link);
xwm->seat = NULL; xwm->seat = NULL;
} }
@ -968,6 +1049,8 @@ void xwm_set_seat(struct wlr_xwm *xwm, struct wlr_seat *seat) {
xwm->seat_selection.notify = seat_handle_selection; xwm->seat_selection.notify = seat_handle_selection;
wl_signal_add(&seat->events.primary_selection, &xwm->seat_primary_selection); wl_signal_add(&seat->events.primary_selection, &xwm->seat_primary_selection);
xwm->seat_primary_selection.notify = seat_handle_primary_selection; xwm->seat_primary_selection.notify = seat_handle_primary_selection;
wl_signal_add(&seat->events.start_drag, &xwm->seat_start_drag);
xwm->seat_start_drag.notify = seat_handle_start_drag;
seat_handle_selection(&xwm->seat_selection, seat); seat_handle_selection(&xwm->seat_selection, seat);
seat_handle_primary_selection(&xwm->seat_primary_selection, seat); seat_handle_primary_selection(&xwm->seat_primary_selection, seat);