blob: a2856b6b3ac786ae948cdf7f7567a3e2b8a55bbe [file] [log] [blame]
Kristian Høgsberg2158a882013-04-18 15:07:39 -04001/*
2 * Copyright © 2011 Kristian Høgsberg
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting documentation, and
8 * that the name of the copyright holders not be used in advertising or
9 * publicity pertaining to distribution of the software without specific,
10 * written prior permission. The copyright holders make no representations
11 * about the suitability of this software for any purpose. It is provided "as
12 * is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20 * OF THIS SOFTWARE.
21 */
22
23#include <stdlib.h>
24#include <string.h>
25#include <unistd.h>
26#include <stdio.h>
27
28#include "compositor.h"
29
30static void
31data_offer_accept(struct wl_client *client, struct wl_resource *resource,
32 uint32_t serial, const char *mime_type)
33{
34 struct wl_data_offer *offer = resource->data;
35
36 /* FIXME: Check that client is currently focused by the input
37 * device that is currently dragging this data source. Should
38 * this be a wl_data_device request? */
39
40 if (offer->source)
41 offer->source->accept(offer->source, serial, mime_type);
42}
43
44static void
45data_offer_receive(struct wl_client *client, struct wl_resource *resource,
46 const char *mime_type, int32_t fd)
47{
48 struct wl_data_offer *offer = resource->data;
49
50 if (offer->source)
51 offer->source->send(offer->source, mime_type, fd);
52 else
53 close(fd);
54}
55
56static void
57data_offer_destroy(struct wl_client *client, struct wl_resource *resource)
58{
59 wl_resource_destroy(resource);
60}
61
62static const struct wl_data_offer_interface data_offer_interface = {
63 data_offer_accept,
64 data_offer_receive,
65 data_offer_destroy,
66};
67
68static void
69destroy_data_offer(struct wl_resource *resource)
70{
71 struct wl_data_offer *offer = resource->data;
72
73 if (offer->source)
74 wl_list_remove(&offer->source_destroy_listener.link);
75 free(offer);
76}
77
78static void
79destroy_offer_data_source(struct wl_listener *listener, void *data)
80{
81 struct wl_data_offer *offer;
82
83 offer = container_of(listener, struct wl_data_offer,
84 source_destroy_listener);
85
86 offer->source = NULL;
87}
88
89static struct wl_resource *
90wl_data_source_send_offer(struct wl_data_source *source,
91 struct wl_resource *target)
92{
93 struct wl_data_offer *offer;
94 char **p;
95
96 offer = malloc(sizeof *offer);
97 if (offer == NULL)
98 return NULL;
99
100 wl_resource_init(&offer->resource, &wl_data_offer_interface,
101 &data_offer_interface, 0, offer);
102 offer->resource.destroy = destroy_data_offer;
103
104 offer->source = source;
105 offer->source_destroy_listener.notify = destroy_offer_data_source;
106 wl_signal_add(&source->resource.destroy_signal,
107 &offer->source_destroy_listener);
108
109 wl_client_add_resource(target->client, &offer->resource);
110
111 wl_data_device_send_data_offer(target, &offer->resource);
112
113 wl_array_for_each(p, &source->mime_types)
114 wl_data_offer_send_offer(&offer->resource, *p);
115
116 return &offer->resource;
117}
118
119static void
120data_source_offer(struct wl_client *client,
121 struct wl_resource *resource,
122 const char *type)
123{
124 struct wl_data_source *source = resource->data;
125 char **p;
126
127 p = wl_array_add(&source->mime_types, sizeof *p);
128 if (p)
129 *p = strdup(type);
130 if (!p || !*p)
131 wl_resource_post_no_memory(resource);
132}
133
134static void
135data_source_destroy(struct wl_client *client, struct wl_resource *resource)
136{
137 wl_resource_destroy(resource);
138}
139
140static struct wl_data_source_interface data_source_interface = {
141 data_source_offer,
142 data_source_destroy
143};
144
145static struct wl_resource *
146find_resource(struct wl_list *list, struct wl_client *client)
147{
148 struct wl_resource *r;
149
150 wl_list_for_each(r, list, link) {
151 if (r->client == client)
152 return r;
153 }
154
155 return NULL;
156}
157
158static void
159destroy_drag_focus(struct wl_listener *listener, void *data)
160{
161 struct wl_seat *seat =
162 container_of(listener, struct wl_seat, drag_focus_listener);
163
164 seat->drag_focus_resource = NULL;
165}
166
167static void
168drag_grab_focus(struct wl_pointer_grab *grab,
169 struct wl_surface *surface, wl_fixed_t x, wl_fixed_t y)
170{
171 struct wl_seat *seat = container_of(grab, struct wl_seat, drag_grab);
172 struct wl_resource *resource, *offer = NULL;
173 struct wl_display *display;
174 uint32_t serial;
175
176 if (seat->drag_focus_resource) {
177 wl_data_device_send_leave(seat->drag_focus_resource);
178 wl_list_remove(&seat->drag_focus_listener.link);
179 seat->drag_focus_resource = NULL;
180 seat->drag_focus = NULL;
181 }
182
183 if (!surface)
184 return;
185
186 if (!seat->drag_data_source &&
187 surface->resource.client != seat->drag_client)
188 return;
189
190 resource = find_resource(&seat->drag_resource_list,
191 surface->resource.client);
192 if (!resource)
193 return;
194
195 display = wl_client_get_display(resource->client);
196 serial = wl_display_next_serial(display);
197
198 if (seat->drag_data_source)
199 offer = wl_data_source_send_offer(seat->drag_data_source,
200 resource);
201
202 wl_data_device_send_enter(resource, serial, &surface->resource,
203 x, y, offer);
204
205 seat->drag_focus = surface;
206 seat->drag_focus_listener.notify = destroy_drag_focus;
207 wl_signal_add(&resource->destroy_signal,
208 &seat->drag_focus_listener);
209 seat->drag_focus_resource = resource;
210 grab->focus = surface;
211}
212
213static void
214drag_grab_motion(struct wl_pointer_grab *grab,
215 uint32_t time, wl_fixed_t x, wl_fixed_t y)
216{
217 struct wl_seat *seat = container_of(grab, struct wl_seat, drag_grab);
218
219 if (seat->drag_focus_resource)
220 wl_data_device_send_motion(seat->drag_focus_resource,
221 time, x, y);
222}
223
224static void
225data_device_end_drag_grab(struct wl_seat *seat)
226{
227 if (seat->drag_surface) {
228 seat->drag_surface = NULL;
229 wl_signal_emit(&seat->drag_icon_signal, NULL);
230 wl_list_remove(&seat->drag_icon_listener.link);
231 }
232
233 drag_grab_focus(&seat->drag_grab, NULL,
234 wl_fixed_from_int(0), wl_fixed_from_int(0));
235
236 wl_pointer_end_grab(seat->pointer);
237
238 seat->drag_data_source = NULL;
239 seat->drag_client = NULL;
240}
241
242static void
243drag_grab_button(struct wl_pointer_grab *grab,
244 uint32_t time, uint32_t button, uint32_t state_w)
245{
246 struct wl_seat *seat = container_of(grab, struct wl_seat, drag_grab);
247 enum wl_pointer_button_state state = state_w;
248
249 if (seat->drag_focus_resource &&
250 seat->pointer->grab_button == button &&
251 state == WL_POINTER_BUTTON_STATE_RELEASED)
252 wl_data_device_send_drop(seat->drag_focus_resource);
253
254 if (seat->pointer->button_count == 0 &&
255 state == WL_POINTER_BUTTON_STATE_RELEASED) {
256 if (seat->drag_data_source)
257 wl_list_remove(&seat->drag_data_source_listener.link);
258 data_device_end_drag_grab(seat);
259 }
260}
261
262static const struct wl_pointer_grab_interface drag_grab_interface = {
263 drag_grab_focus,
264 drag_grab_motion,
265 drag_grab_button,
266};
267
268static void
269destroy_data_device_source(struct wl_listener *listener, void *data)
270{
271 struct wl_seat *seat = container_of(listener, struct wl_seat,
272 drag_data_source_listener);
273
274 data_device_end_drag_grab(seat);
275}
276
277static void
278destroy_data_device_icon(struct wl_listener *listener, void *data)
279{
280 struct wl_seat *seat = container_of(listener, struct wl_seat,
281 drag_icon_listener);
282
283 seat->drag_surface = NULL;
284}
285
286static void
287data_device_start_drag(struct wl_client *client, struct wl_resource *resource,
288 struct wl_resource *source_resource,
289 struct wl_resource *origin_resource,
290 struct wl_resource *icon_resource, uint32_t serial)
291{
292 struct wl_seat *seat = resource->data;
293
294 /* FIXME: Check that client has implicit grab on the origin
295 * surface that matches the given time. */
296
297 /* FIXME: Check that the data source type array isn't empty. */
298
299 seat->drag_grab.interface = &drag_grab_interface;
300
301 seat->drag_client = client;
302 seat->drag_data_source = NULL;
303
304 if (source_resource) {
305 seat->drag_data_source = source_resource->data;
306 seat->drag_data_source_listener.notify =
307 destroy_data_device_source;
308 wl_signal_add(&source_resource->destroy_signal,
309 &seat->drag_data_source_listener);
310 }
311
312 if (icon_resource) {
313 seat->drag_surface = icon_resource->data;
314 seat->drag_icon_listener.notify = destroy_data_device_icon;
315 wl_signal_add(&icon_resource->destroy_signal,
316 &seat->drag_icon_listener);
317 wl_signal_emit(&seat->drag_icon_signal, icon_resource);
318 }
319
320 wl_pointer_set_focus(seat->pointer, NULL,
321 wl_fixed_from_int(0), wl_fixed_from_int(0));
322 wl_pointer_start_grab(seat->pointer, &seat->drag_grab);
323}
324
325static void
326destroy_selection_data_source(struct wl_listener *listener, void *data)
327{
328 struct wl_seat *seat = container_of(listener, struct wl_seat,
329 selection_data_source_listener);
330 struct wl_resource *data_device;
331 struct wl_resource *focus = NULL;
332
333 seat->selection_data_source = NULL;
334
335 if (seat->keyboard)
336 focus = seat->keyboard->focus_resource;
337 if (focus) {
338 data_device = find_resource(&seat->drag_resource_list,
339 focus->client);
340 if (data_device)
341 wl_data_device_send_selection(data_device, NULL);
342 }
343
344 wl_signal_emit(&seat->selection_signal, seat);
345}
346
347WL_EXPORT void
348wl_seat_set_selection(struct wl_seat *seat, struct wl_data_source *source,
349 uint32_t serial)
350{
351 struct wl_resource *data_device, *offer;
352 struct wl_resource *focus = NULL;
353
354 if (seat->selection_data_source &&
355 seat->selection_serial - serial < UINT32_MAX / 2)
356 return;
357
358 if (seat->selection_data_source) {
359 seat->selection_data_source->cancel(seat->selection_data_source);
360 wl_list_remove(&seat->selection_data_source_listener.link);
361 seat->selection_data_source = NULL;
362 }
363
364 seat->selection_data_source = source;
365 seat->selection_serial = serial;
366
367 if (seat->keyboard)
368 focus = seat->keyboard->focus_resource;
369 if (focus) {
370 data_device = find_resource(&seat->drag_resource_list,
371 focus->client);
372 if (data_device && source) {
373 offer = wl_data_source_send_offer(seat->selection_data_source,
374 data_device);
375 wl_data_device_send_selection(data_device, offer);
376 } else if (data_device) {
377 wl_data_device_send_selection(data_device, NULL);
378 }
379 }
380
381 wl_signal_emit(&seat->selection_signal, seat);
382
383 if (source) {
384 seat->selection_data_source_listener.notify =
385 destroy_selection_data_source;
386 wl_signal_add(&source->resource.destroy_signal,
387 &seat->selection_data_source_listener);
388 }
389}
390
391static void
392data_device_set_selection(struct wl_client *client,
393 struct wl_resource *resource,
394 struct wl_resource *source_resource, uint32_t serial)
395{
396 if (!source_resource)
397 return;
398
399 /* FIXME: Store serial and check against incoming serial here. */
400 wl_seat_set_selection(resource->data, source_resource->data,
401 serial);
402}
403
404static const struct wl_data_device_interface data_device_interface = {
405 data_device_start_drag,
406 data_device_set_selection,
407};
408
409static void
410destroy_data_source(struct wl_resource *resource)
411{
412 struct wl_data_source *source =
413 container_of(resource, struct wl_data_source, resource);
414 char **p;
415
416 wl_array_for_each(p, &source->mime_types)
417 free(*p);
418
419 wl_array_release(&source->mime_types);
420
421 source->resource.object.id = 0;
422}
423
424static void
425client_source_accept(struct wl_data_source *source,
426 uint32_t time, const char *mime_type)
427{
428 wl_data_source_send_target(&source->resource, mime_type);
429}
430
431static void
432client_source_send(struct wl_data_source *source,
433 const char *mime_type, int32_t fd)
434{
435 wl_data_source_send_send(&source->resource, mime_type, fd);
436 close(fd);
437}
438
439static void
440client_source_cancel(struct wl_data_source *source)
441{
442 wl_data_source_send_cancelled(&source->resource);
443}
444
445static void
446create_data_source(struct wl_client *client,
447 struct wl_resource *resource, uint32_t id)
448{
449 struct wl_data_source *source;
450
451 source = malloc(sizeof *source);
452 if (source == NULL) {
453 wl_resource_post_no_memory(resource);
454 return;
455 }
456
457 wl_resource_init(&source->resource, &wl_data_source_interface,
458 &data_source_interface, id, source);
459 source->resource.destroy = destroy_data_source;
460
461 source->accept = client_source_accept;
462 source->send = client_source_send;
463 source->cancel = client_source_cancel;
464
465 wl_array_init(&source->mime_types);
466 wl_client_add_resource(client, &source->resource);
467}
468
469static void unbind_data_device(struct wl_resource *resource)
470{
471 wl_list_remove(&resource->link);
472 free(resource);
473}
474
475static void
476get_data_device(struct wl_client *client,
477 struct wl_resource *manager_resource,
478 uint32_t id, struct wl_resource *seat_resource)
479{
480 struct wl_seat *seat = seat_resource->data;
481 struct wl_resource *resource;
482
483 resource = wl_client_add_object(client, &wl_data_device_interface,
484 &data_device_interface, id,
485 seat);
486
487 wl_list_insert(&seat->drag_resource_list, &resource->link);
488 resource->destroy = unbind_data_device;
489}
490
491static const struct wl_data_device_manager_interface manager_interface = {
492 create_data_source,
493 get_data_device
494};
495
496static void
497bind_manager(struct wl_client *client,
498 void *data, uint32_t version, uint32_t id)
499{
500 wl_client_add_object(client, &wl_data_device_manager_interface,
501 &manager_interface, id, NULL);
502}
503
504WL_EXPORT void
505wl_data_device_set_keyboard_focus(struct wl_seat *seat)
506{
507 struct wl_resource *data_device, *focus, *offer;
508 struct wl_data_source *source;
509
510 if (!seat->keyboard)
511 return;
512
513 focus = seat->keyboard->focus_resource;
514 if (!focus)
515 return;
516
517 data_device = find_resource(&seat->drag_resource_list,
518 focus->client);
519 if (!data_device)
520 return;
521
522 source = seat->selection_data_source;
523 if (source) {
524 offer = wl_data_source_send_offer(source, data_device);
525 wl_data_device_send_selection(data_device, offer);
526 }
527}
528
529WL_EXPORT int
530wl_data_device_manager_init(struct wl_display *display)
531{
532 if (wl_display_add_global(display,
533 &wl_data_device_manager_interface,
534 NULL, bind_manager) == NULL)
535 return -1;
536
537 return 0;
538}