1 /*
2 * Copyright © 2013 Intel Corporation
3 *
4 * 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:
11 *
12 * 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.
24 */
25
26 #include "config.h"
27
28 #include <stdlib.h>
29 #include <stdint.h>
30 #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
40 #include <libweston/libweston.h>
41 #include "xwayland.h"
42
43 #include "hash.h"
44
45 struct dnd_data_source {
46 struct weston_data_source base;
47 struct weston_wm *wm;
48 int version;
49 uint32_t window;
50 };
51
52 static void
data_source_accept(struct weston_data_source * base,uint32_t time,const char * mime_type)53 data_source_accept(struct weston_data_source *base,
54 uint32_t time, const char *mime_type)
55 {
56 struct dnd_data_source *source = (struct dnd_data_source *) base;
57 xcb_client_message_event_t client_message;
58 struct weston_wm *wm = source->wm;
59
60 weston_log("got accept, mime-type %s\n", mime_type);
61
62 /* FIXME: If we rewrote UTF8_STRING to
63 * text/plain;charset=utf-8 and the source doesn't support the
64 * mime-type, we'll have to rewrite the mime-type back to
65 * UTF8_STRING here. */
66
67 client_message.response_type = XCB_CLIENT_MESSAGE;
68 client_message.format = 32;
69 client_message.window = wm->dnd_window;
70 client_message.type = wm->atom.xdnd_status;
71 client_message.data.data32[0] = wm->dnd_window;
72 client_message.data.data32[1] = 2;
73 if (mime_type)
74 client_message.data.data32[1] |= 1;
75 client_message.data.data32[2] = 0;
76 client_message.data.data32[3] = 0;
77 client_message.data.data32[4] = wm->atom.xdnd_action_copy;
78
79 xcb_send_event(wm->conn, 0, wm->dnd_owner,
80 XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
81 (char *) &client_message);
82 }
83
84 static void
data_source_send(struct weston_data_source * base,const char * mime_type,int32_t fd)85 data_source_send(struct weston_data_source *base,
86 const char *mime_type, int32_t fd)
87 {
88 struct dnd_data_source *source = (struct dnd_data_source *) base;
89 struct weston_wm *wm = source->wm;
90
91 weston_log("got send, %s\n", mime_type);
92
93 /* Get data for the utf8_string target */
94 xcb_convert_selection(wm->conn,
95 wm->selection_window,
96 wm->atom.xdnd_selection,
97 wm->atom.utf8_string,
98 wm->atom.wl_selection,
99 XCB_TIME_CURRENT_TIME);
100
101 xcb_flush(wm->conn);
102
103 fcntl(fd, F_SETFL, O_WRONLY | O_NONBLOCK);
104 wm->data_source_fd = fd;
105 }
106
107 static void
data_source_cancel(struct weston_data_source * source)108 data_source_cancel(struct weston_data_source *source)
109 {
110 weston_log("got cancel\n");
111 }
112
113 static void
handle_enter(struct weston_wm * wm,xcb_client_message_event_t * client_message)114 handle_enter(struct weston_wm *wm, xcb_client_message_event_t *client_message)
115 {
116 struct dnd_data_source *source;
117 struct weston_seat *seat = weston_wm_pick_seat(wm);
118 struct weston_pointer *pointer = weston_seat_get_pointer(seat);
119 char **p;
120 const char *name;
121 uint32_t *types;
122 int i, length, has_text;
123 xcb_get_property_cookie_t cookie;
124 xcb_get_property_reply_t *reply;
125
126 source = zalloc(sizeof *source);
127 if (source == NULL)
128 return;
129
130 wl_signal_init(&source->base.destroy_signal);
131 source->base.accept = data_source_accept;
132 source->base.send = data_source_send;
133 source->base.cancel = data_source_cancel;
134 source->wm = wm;
135 source->window = client_message->data.data32[0];
136 source->version = client_message->data.data32[1] >> 24;
137
138 if (client_message->data.data32[1] & 1) {
139 cookie = xcb_get_property(wm->conn,
140 0, /* delete */
141 source->window,
142 wm->atom.xdnd_type_list,
143 XCB_ATOM_ANY, 0, 2048);
144 reply = xcb_get_property_reply(wm->conn, cookie, NULL);
145 types = xcb_get_property_value(reply);
146 length = reply->value_len;
147 } else {
148 reply = NULL;
149 types = &client_message->data.data32[2];
150 length = 3;
151 }
152
153 wl_array_init(&source->base.mime_types);
154 has_text = 0;
155 for (i = 0; i < length; i++) {
156 if (types[i] == XCB_ATOM_NONE)
157 continue;
158
159 name = get_atom_name(wm->conn, types[i]);
160 if (types[i] == wm->atom.utf8_string ||
161 types[i] == wm->atom.text_plain_utf8 ||
162 types[i] == wm->atom.text_plain) {
163 if (has_text)
164 continue;
165
166 has_text = 1;
167 p = wl_array_add(&source->base.mime_types, sizeof *p);
168 if (p)
169 *p = strdup("text/plain;charset=utf-8");
170 } else if (strchr(name, '/')) {
171 p = wl_array_add(&source->base.mime_types, sizeof *p);
172 if (p)
173 *p = strdup(name);
174 }
175 }
176
177 free(reply);
178 weston_pointer_start_drag(pointer, &source->base, NULL, NULL);
179 }
180
181 int
weston_wm_handle_dnd_event(struct weston_wm * wm,xcb_generic_event_t * event)182 weston_wm_handle_dnd_event(struct weston_wm *wm,
183 xcb_generic_event_t *event)
184 {
185 xcb_xfixes_selection_notify_event_t *xfixes_selection_notify =
186 (xcb_xfixes_selection_notify_event_t *) event;
187 xcb_client_message_event_t *client_message =
188 (xcb_client_message_event_t *) event;
189
190 switch (event->response_type - wm->xfixes->first_event) {
191 case XCB_XFIXES_SELECTION_NOTIFY:
192 if (xfixes_selection_notify->selection != wm->atom.xdnd_selection)
193 return 0;
194
195 weston_log("XdndSelection owner: %d!\n",
196 xfixes_selection_notify->owner);
197 return 1;
198 }
199
200 switch (EVENT_TYPE(event)) {
201 case XCB_CLIENT_MESSAGE:
202 if (client_message->type == wm->atom.xdnd_enter) {
203 handle_enter(wm, client_message);
204 return 1;
205 } else if (client_message->type == wm->atom.xdnd_leave) {
206 weston_log("got leave!\n");
207 return 1;
208 } else if (client_message->type == wm->atom.xdnd_drop) {
209 weston_log("got drop!\n");
210 return 1;
211 } else if (client_message->type == wm->atom.xdnd_drop) {
212 weston_log("got enter!\n");
213 return 1;
214 }
215 return 0;
216 }
217
218 return 0;
219 }
220
221 void
weston_wm_dnd_init(struct weston_wm * wm)222 weston_wm_dnd_init(struct weston_wm *wm)
223 {
224 uint32_t values[1], version = 4, mask;
225
226 mask =
227 XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER |
228 XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY |
229 XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE;
230 xcb_xfixes_select_selection_input(wm->conn, wm->selection_window,
231 wm->atom.xdnd_selection, mask);
232
233 wm->dnd_window = xcb_generate_id(wm->conn);
234 values[0] =
235 XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
236 XCB_EVENT_MASK_PROPERTY_CHANGE;
237
238 xcb_create_window(wm->conn,
239 XCB_COPY_FROM_PARENT,
240 wm->dnd_window,
241 wm->screen->root,
242 0, 0,
243 8192, 8192,
244 0,
245 XCB_WINDOW_CLASS_INPUT_ONLY,
246 wm->screen->root_visual,
247 XCB_CW_EVENT_MASK, values);
248 xcb_change_property(wm->conn,
249 XCB_PROP_MODE_REPLACE,
250 wm->dnd_window,
251 wm->atom.xdnd_aware,
252 XCB_ATOM_ATOM,
253 32, /* format */
254 1, &version);
255 }
256