|
|
@ -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);
|
|
|
|