blob: 41727b8c4895a8453ed6516a2cfc86702afa7129 [file] [log] [blame]
Kristian Høgsbergf9cb3b12013-09-04 21:12:26 -07001/*
2 * Copyright © 2013 Intel Corporation
3 *
Bryce Harrington0a007dd2015-06-11 16:22:34 -07004 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
Kristian Høgsbergf9cb3b12013-09-04 21:12:26 -070011 *
Bryce Harrington0a007dd2015-06-11 16:22:34 -070012 * The above copyright notice and this permission notice (including the
13 * next paragraph) shall be included in all copies or substantial
14 * portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
Kristian Høgsbergf9cb3b12013-09-04 21:12:26 -070024 */
25
26#include "config.h"
27
28#include <stdlib.h>
Jussi Kukkonen649bbce2016-07-19 14:16:27 +030029#include <stdint.h>
Kristian Høgsbergf9cb3b12013-09-04 21:12:26 -070030#include <stdio.h>
31#include <string.h>
32#include <sys/socket.h>
33#include <sys/un.h>
34#include <fcntl.h>
35#include <errno.h>
36#include <unistd.h>
37#include <signal.h>
38#include <X11/Xcursor/Xcursor.h>
39
Daniel Stone67fe3db2016-10-31 14:51:18 +000040#include "compositor.h"
Kristian Høgsbergf9cb3b12013-09-04 21:12:26 -070041#include "xwayland.h"
42
Kristian Høgsberg2ba10df2013-12-03 16:38:15 -080043#include "cairo-util.h"
Kristian Høgsbergf9cb3b12013-09-04 21:12:26 -070044#include "hash.h"
45
46static void
47weston_dnd_start(struct weston_wm *wm, xcb_window_t owner)
48{
49 uint32_t values[1], version = 4;
50
51 wm->dnd_window = xcb_generate_id(wm->conn);
52 values[0] =
53 XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
54 XCB_EVENT_MASK_PROPERTY_CHANGE;
55
56 xcb_create_window(wm->conn,
57 XCB_COPY_FROM_PARENT,
58 wm->dnd_window,
59 wm->screen->root,
60 0, 0,
61 8192, 8192,
62 0,
63 XCB_WINDOW_CLASS_INPUT_ONLY,
64 wm->screen->root_visual,
65 XCB_CW_EVENT_MASK, values);
66 xcb_change_property(wm->conn,
67 XCB_PROP_MODE_REPLACE,
68 wm->dnd_window,
69 wm->atom.xdnd_aware,
70 XCB_ATOM_ATOM,
71 32, /* format */
72 1, &version);
73
74 xcb_map_window(wm->conn, wm->dnd_window);
75 wm->dnd_owner = owner;
76}
77
78static void
79weston_dnd_stop(struct weston_wm *wm)
80{
81 xcb_destroy_window(wm->conn, wm->dnd_window);
82 wm->dnd_window = XCB_WINDOW_NONE;
83}
84
85struct dnd_data_source {
86 struct weston_data_source base;
87 struct weston_wm *wm;
88 int version;
89 uint32_t window;
90};
91
92static void
93data_source_accept(struct weston_data_source *base,
94 uint32_t time, const char *mime_type)
95{
96 struct dnd_data_source *source = (struct dnd_data_source *) base;
97 xcb_client_message_event_t client_message;
98 struct weston_wm *wm = source->wm;
99
100 weston_log("got accept, mime-type %s\n", mime_type);
101
102 /* FIXME: If we rewrote UTF8_STRING to
103 * text/plain;charset=utf-8 and the source doesn't support the
104 * mime-type, we'll have to rewrite the mime-type back to
105 * UTF8_STRING here. */
106
107 client_message.response_type = XCB_CLIENT_MESSAGE;
108 client_message.format = 32;
109 client_message.window = wm->dnd_window;
110 client_message.type = wm->atom.xdnd_status;
111 client_message.data.data32[0] = wm->dnd_window;
112 client_message.data.data32[1] = 2;
113 if (mime_type)
114 client_message.data.data32[1] |= 1;
115 client_message.data.data32[2] = 0;
116 client_message.data.data32[3] = 0;
117 client_message.data.data32[4] = wm->atom.xdnd_action_copy;
118
119 xcb_send_event(wm->conn, 0, wm->dnd_owner,
120 XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
121 (char *) &client_message);
122}
123
124static void
125data_source_send(struct weston_data_source *base,
126 const char *mime_type, int32_t fd)
127{
128 struct dnd_data_source *source = (struct dnd_data_source *) base;
129 struct weston_wm *wm = source->wm;
130
131 weston_log("got send, %s\n", mime_type);
132
133 /* Get data for the utf8_string target */
134 xcb_convert_selection(wm->conn,
135 wm->selection_window,
136 wm->atom.xdnd_selection,
137 wm->atom.utf8_string,
138 wm->atom.wl_selection,
139 XCB_TIME_CURRENT_TIME);
140
141 xcb_flush(wm->conn);
142
143 fcntl(fd, F_SETFL, O_WRONLY | O_NONBLOCK);
144 wm->data_source_fd = fd;
145}
146
147static void
148data_source_cancel(struct weston_data_source *source)
149{
150 weston_log("got cancel\n");
151}
152
153static void
154handle_enter(struct weston_wm *wm, xcb_client_message_event_t *client_message)
155{
156 struct dnd_data_source *source;
157 struct weston_seat *seat = weston_wm_pick_seat(wm);
Derek Foreman1281a362015-07-31 16:55:32 -0500158 struct weston_pointer *pointer = weston_seat_get_pointer(seat);
Kristian Høgsbergf9cb3b12013-09-04 21:12:26 -0700159 char **p;
160 const char *name;
161 uint32_t *types;
162 int i, length, has_text;
163 xcb_get_property_cookie_t cookie;
164 xcb_get_property_reply_t *reply;
165
Carlos Garnachobeb7a9f2016-02-01 20:28:15 +0100166 source = zalloc(sizeof *source);
Kristian Høgsbergf9cb3b12013-09-04 21:12:26 -0700167 if (source == NULL)
168 return;
169
170 wl_signal_init(&source->base.destroy_signal);
171 source->base.accept = data_source_accept;
172 source->base.send = data_source_send;
173 source->base.cancel = data_source_cancel;
174 source->wm = wm;
175 source->window = client_message->data.data32[0];
176 source->version = client_message->data.data32[1] >> 24;
177
178 if (client_message->data.data32[1] & 1) {
179 cookie = xcb_get_property(wm->conn,
180 0, /* delete */
181 source->window,
182 wm->atom.xdnd_type_list,
183 XCB_ATOM_ANY, 0, 2048);
184 reply = xcb_get_property_reply(wm->conn, cookie, NULL);
185 types = xcb_get_property_value(reply);
186 length = reply->value_len;
187 } else {
188 reply = NULL;
189 types = &client_message->data.data32[2];
190 length = 3;
191 }
192
193 wl_array_init(&source->base.mime_types);
194 has_text = 0;
195 for (i = 0; i < length; i++) {
196 if (types[i] == XCB_ATOM_NONE)
197 continue;
198
199 name = get_atom_name(wm->conn, types[i]);
200 if (types[i] == wm->atom.utf8_string ||
201 types[i] == wm->atom.text_plain_utf8 ||
202 types[i] == wm->atom.text_plain) {
203 if (has_text)
204 continue;
205
206 has_text = 1;
207 p = wl_array_add(&source->base.mime_types, sizeof *p);
208 if (p)
209 *p = strdup("text/plain;charset=utf-8");
210 } else if (strchr(name, '/')) {
211 p = wl_array_add(&source->base.mime_types, sizeof *p);
212 if (p)
213 *p = strdup(name);
214 }
215 }
216
217 free(reply);
Derek Foreman1281a362015-07-31 16:55:32 -0500218 weston_pointer_start_drag(pointer, &source->base, NULL, NULL);
Kristian Høgsbergf9cb3b12013-09-04 21:12:26 -0700219}
220
221int
222weston_wm_handle_dnd_event(struct weston_wm *wm,
223 xcb_generic_event_t *event)
224{
225 xcb_xfixes_selection_notify_event_t *xfixes_selection_notify =
226 (xcb_xfixes_selection_notify_event_t *) event;
227 xcb_client_message_event_t *client_message =
228 (xcb_client_message_event_t *) event;
229
230 switch (event->response_type - wm->xfixes->first_event) {
231 case XCB_XFIXES_SELECTION_NOTIFY:
232 if (xfixes_selection_notify->selection != wm->atom.xdnd_selection)
233 return 0;
234
235 weston_log("XdndSelection owner: %d!\n",
236 xfixes_selection_notify->owner);
237
238 if (xfixes_selection_notify->owner != XCB_WINDOW_NONE)
239 weston_dnd_start(wm, xfixes_selection_notify->owner);
240 else
241 weston_dnd_stop(wm);
242
243 return 1;
244 }
245
246 switch (EVENT_TYPE(event)) {
247 case XCB_CLIENT_MESSAGE:
248 if (client_message->type == wm->atom.xdnd_enter) {
249 handle_enter(wm, client_message);
250 return 1;
251 } else if (client_message->type == wm->atom.xdnd_leave) {
252 weston_log("got leave!\n");
253 return 1;
254 } else if (client_message->type == wm->atom.xdnd_drop) {
255 weston_log("got drop!\n");
256 return 1;
257 } else if (client_message->type == wm->atom.xdnd_drop) {
258 weston_log("got enter!\n");
259 return 1;
260 }
261 return 0;
262 }
263
264 return 0;
265}
266
267void
268weston_wm_dnd_init(struct weston_wm *wm)
269{
270 uint32_t mask;
271
272 mask =
273 XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER |
274 XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY |
275 XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE;
276 xcb_xfixes_select_selection_input(wm->conn, wm->selection_window,
277 wm->atom.xdnd_selection, mask);
278}