Update drag protocol to use fd passing for data transfer
[profile/ivi/weston.git] / clients / dnd.c
1 /*
2  * Copyright © 2010 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 <stdint.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <fcntl.h>
28 #include <unistd.h>
29 #include <math.h>
30 #include <sys/time.h>
31 #include <cairo.h>
32 #include <glib.h>
33 #include <gdk-pixbuf/gdk-pixbuf.h>
34
35 #include "wayland-client.h"
36 #include "wayland-glib.h"
37
38 #include "window.h"
39
40 static const char socket_name[] = "\0wayland";
41
42 struct dnd {
43         struct window *window;
44         struct display *display;
45         uint32_t key;
46         struct item *items[16];
47
48         struct wl_buffer *buffer;
49         int hotspot_x, hotspot_y;
50         uint32_t tag;
51         const char *drag_type;
52 };
53
54 struct item {
55         cairo_surface_t *surface;
56         int x, y;
57 };
58
59 static const int item_width = 64;
60 static const int item_height = 64;
61 static const int item_padding = 16;
62
63 static struct item *
64 item_create(struct display *display, int x, int y)
65 {
66         const int petal_count = 3 + random() % 5;
67         const double r1 = 20 + random() % 10;
68         const double r2 = 5 + random() % 12;
69         const double u = (10 + random() % 90) / 100.0;
70         const double v = (random() % 90) / 100.0;
71
72         cairo_t *cr;
73         int i;
74         double t, dt = 2 * M_PI / (petal_count * 2);
75         double x1, y1, x2, y2, x3, y3;
76         struct rectangle rect;
77         struct item *item;
78
79         item = malloc(sizeof *item);
80         if (item == NULL)
81                 return NULL;
82
83         rect.width = item_width;
84         rect.height = item_height;
85         item->surface = display_create_surface(display, &rect);
86
87         item->x = x;
88         item->y = y;
89
90         cr = cairo_create(item->surface);
91         cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
92         cairo_set_source_rgba(cr, 0, 0, 0, 0);
93         cairo_paint(cr);
94
95         cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
96         cairo_translate(cr, item_width / 2, item_height / 2);
97         t = random();
98         cairo_move_to(cr, cos(t) * r1, sin(t) * r1);
99         for (i = 0; i < petal_count; i++, t += dt * 2) {
100                 x1 = cos(t) * r1;
101                 y1 = sin(t) * r1;
102                 x2 = cos(t + dt) * r2;
103                 y2 = sin(t + dt) * r2;
104                 x3 = cos(t + 2 * dt) * r1;
105                 y3 = sin(t + 2 * dt) * r1;
106
107                 cairo_curve_to(cr,
108                                x1 - y1 * u, y1 + x1 * u,
109                                x2 + y2 * v, y2 - x2 * v,
110                                x2, y2);                        
111
112                 cairo_curve_to(cr,
113                                x2 - y2 * v, y2 + x2 * v,
114                                x3 + y3 * u, y3 - x3 * u,
115                                x3, y3);
116         }
117
118         cairo_close_path(cr);
119
120         cairo_set_source_rgba(cr,
121                               0.5 + (random() % 50) / 49.0,
122                               0.5 + (random() % 50) / 49.0,
123                               0.5 + (random() % 50) / 49.0,
124                               0.5 + (random() % 100) / 99.0);
125
126         cairo_fill_preserve(cr);
127
128         cairo_set_line_width(cr, 1);
129         cairo_set_source_rgba(cr,
130                               0.5 + (random() % 50) / 49.0,
131                               0.5 + (random() % 50) / 49.0,
132                               0.5 + (random() % 50) / 49.0,
133                               0.5 + (random() % 100) / 99.0);
134         cairo_stroke(cr);
135
136         cairo_destroy(cr);
137
138         return item;
139 }
140
141 static void
142 dnd_draw(struct dnd *dnd)
143 {
144         struct rectangle rectangle;
145         cairo_t *cr;
146         cairo_surface_t *wsurface, *surface;
147         int i;
148
149         window_draw(dnd->window);
150
151         window_get_child_rectangle(dnd->window, &rectangle);
152
153         wsurface = window_get_surface(dnd->window);
154         surface = cairo_surface_create_similar(wsurface,
155                                                CAIRO_CONTENT_COLOR_ALPHA,
156                                                rectangle.width,
157                                                rectangle.height);
158
159         cr = cairo_create(surface);
160         cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
161         cairo_set_source_rgba(cr, 0, 0, 0, 0.8);
162         cairo_paint(cr);
163
164         cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
165         for (i = 0; i < ARRAY_LENGTH(dnd->items); i++) {
166                 if (!dnd->items[i])
167                         continue;
168                 cairo_set_source_surface(cr, dnd->items[i]->surface,
169                                          dnd->items[i]->x, dnd->items[i]->y);
170                 cairo_paint(cr);
171         }
172
173         cairo_destroy(cr);
174
175         window_copy_surface(dnd->window, &rectangle, surface);
176         window_commit(dnd->window, dnd->key);
177         cairo_surface_destroy(surface);
178 }
179
180 static void
181 redraw_handler(struct window *window, void *data)
182 {
183         struct dnd *dnd = data;
184
185         dnd_draw(dnd);
186 }
187
188 static void
189 keyboard_focus_handler(struct window *window,
190                        struct input *device, void *data)
191 {
192         struct dnd *dnd = data;
193
194         window_schedule_redraw(dnd->window);
195 }
196
197 static struct item *
198 dnd_get_item(struct dnd *dnd, int32_t x, int32_t y)
199 {
200         struct item *item;
201         int i;
202
203         for (i = 0; i < ARRAY_LENGTH(dnd->items); i++) {
204                 item = dnd->items[i];
205                 if (item &&
206                     item->x <= x && x < item->x + item_width &&
207                     item->y <= y && y < item->y + item_height)
208                         return item;
209         }
210
211         return NULL;
212 }
213
214 static void
215 drag_handle_device(void *data,
216                    struct wl_drag *drag, struct wl_input_device *device)
217 {
218 }
219
220 static void
221 drag_pointer_focus(void *data,
222                    struct wl_drag *drag,
223                    uint32_t time, struct wl_surface *surface,
224                    int32_t x, int32_t y, int32_t surface_x, int32_t surface_y)
225 {
226         struct dnd *dnd = data;
227
228         /* FIXME: We need the offered types before we get the
229          * pointer_focus event so we know which one we want and can
230          * send the accept request back. */
231
232         fprintf(stderr, "drag pointer focus %p\n", surface);
233
234         if (surface) {
235                 wl_drag_accept(drag, "text/plain");
236                 dnd->drag_type = "text/plain";
237         } else {
238                 dnd->drag_type = NULL;
239         }
240 }
241
242 static void
243 drag_offer(void *data,
244            struct wl_drag *drag, const char *type)
245 {
246         fprintf(stderr, "drag offer %s\n", type);
247 }
248
249 static void
250 drag_motion(void *data,
251             struct wl_drag *drag,
252             uint32_t time,
253             int32_t x, int32_t y, int32_t surface_x, int32_t surface_y)
254 {
255         struct dnd *dnd = data;
256
257         /* FIXME: Need to correlate this with the offer event.
258          * Problem is, we don't know when we've seen that last offer
259          * event, and we might need to look at all of them before we
260          * can decide which one to go with. */
261         wl_drag_accept(drag, "text/plain");
262         dnd->drag_type = "text/plain";
263 }
264
265 static void
266 drag_target(void *data,
267             struct wl_drag *drag, const char *mime_type)
268 {
269         struct dnd *dnd = data;
270         struct input *input;
271         struct wl_input_device *device;
272
273         input = wl_drag_get_user_data(drag);
274         device = input_get_input_device(input);
275         wl_input_device_attach(device, dnd->buffer,
276                                dnd->hotspot_x, dnd->hotspot_y);
277 }
278
279 static gboolean
280 drop_io_func(GIOChannel *source, GIOCondition condition, gpointer data)
281 {
282         struct dnd *dnd = data;
283         char buffer[256];
284         int fd;
285         unsigned int len;
286         GError *err = NULL;
287
288         g_io_channel_read_chars(source, buffer, sizeof buffer, &len, &err);
289         fprintf(stderr, "read %d bytes: %s\n", len, buffer);
290         fd = g_io_channel_unix_get_fd(source);
291         close(fd);
292         g_source_remove(dnd->tag);
293
294         g_io_channel_unref(source);
295
296         return TRUE;
297 }
298
299 static void
300 drag_drop(void *data, struct wl_drag *drag)
301 {
302         struct dnd *dnd = data;
303         int p[2];
304         GIOChannel *channel;
305
306         if (!dnd->drag_type) {
307                 fprintf(stderr, "got 'drop', but no target\n");
308                 return;
309         }
310
311         fprintf(stderr, "got 'drop', sending write end of pipe\n");
312
313         pipe(p);
314         wl_drag_receive(drag, p[1]);
315         close(p[1]);
316
317         channel = g_io_channel_unix_new(p[0]);
318         dnd->tag = g_io_add_watch(channel, G_IO_IN, drop_io_func, dnd);
319 }
320
321 static void
322 drag_finish(void *data, struct wl_drag *drag, int fd)
323 {
324         char text[] = "[drop data]";
325
326         fprintf(stderr, "got 'finish', fd %d, sending message\n", fd);
327
328         write(fd, text, sizeof text);
329         close(fd);
330 }
331
332 static const struct wl_drag_listener drag_listener = {
333         drag_handle_device,
334         drag_pointer_focus,
335         drag_offer,
336         drag_motion,
337         drag_target,
338         drag_drop,
339         drag_finish
340 };
341
342 static void
343 dnd_button_handler(struct window *window,
344                    struct input *input, uint32_t time,
345                    int button, int state, void *data)
346 {
347         struct dnd *dnd = data;
348         int32_t x, y, hotspot_x, hotspot_y, pointer_width, pointer_height;
349         struct rectangle rectangle;
350         struct item *item;
351         cairo_surface_t *surface, *pointer;
352         cairo_t *cr;
353
354         input_get_position(input, &x, &y);
355         window_get_child_rectangle(dnd->window, &rectangle);
356
357         x -= rectangle.x;
358         y -= rectangle.y;
359         item = dnd_get_item(dnd, x, y);
360
361         if (item && state == 1) {
362                 fprintf(stderr, "start drag, item %p\n", item);
363
364                 pointer = display_get_pointer_surface(dnd->display,
365                                                       POINTER_DRAGGING,
366                                                       &pointer_width,
367                                                       &pointer_height,
368                                                       &hotspot_x,
369                                                       &hotspot_y);
370
371                 rectangle.width = item_width + 2 * pointer_width;
372                 rectangle.height = item_height + 2 * pointer_height;
373                 surface = display_create_surface(dnd->display, &rectangle);
374
375                 cr = cairo_create(surface);
376                 cairo_translate(cr, pointer_width, pointer_height);
377
378                 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
379                 cairo_set_source_rgba(cr, 0, 0, 0, 0);
380                 cairo_paint(cr);
381
382                 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
383                 cairo_set_source_surface(cr, item->surface, 0, 0);
384                 cairo_paint(cr);
385
386                 cairo_set_source_surface(cr, pointer,
387                                          x - item->x - hotspot_x,
388                                          y - item->y - hotspot_y);
389                 cairo_surface_destroy(pointer);
390                 cairo_paint(cr);
391                 cairo_destroy(cr);
392
393                 dnd->buffer = display_get_buffer_for_surface(dnd->display,
394                                                              surface);
395                 dnd->hotspot_x = pointer_width + x - item->x;
396                 dnd->hotspot_y = pointer_height + y - item->y;
397
398                 window_start_drag(window, input, time);
399
400                 /* FIXME: We leak the surface because we can't free it
401                  * until the server has referenced it. */
402         }
403 }
404
405 static int
406 dnd_motion_handler(struct window *window,
407                    struct input *input, uint32_t time,
408                    int32_t x, int32_t y,
409                    int32_t sx, int32_t sy, void *data)
410 {
411         struct dnd *dnd = data;
412         struct item *item;
413         struct rectangle rectangle;
414
415         window_get_child_rectangle(dnd->window, &rectangle);
416         item = dnd_get_item(dnd, sx - rectangle.x, sy - rectangle.y);
417
418         if (item)
419                 return POINTER_HAND1;
420         else
421                 return POINTER_LEFT_PTR;
422 }
423
424 static struct dnd *
425 dnd_create(struct display *display)
426 {
427         struct dnd *dnd;
428         gchar *title;
429         int i, x, y;
430         struct rectangle rectangle;
431
432         dnd = malloc(sizeof *dnd);
433         if (dnd == NULL)
434                 return dnd;
435         memset(dnd, 0, sizeof *dnd);
436
437         title = g_strdup_printf("Wayland Drag and Drop Demo");
438
439         dnd->window = window_create(display, title, 100, 100, 500, 400);
440         dnd->display = display;
441         dnd->key = 100;
442
443         for (i = 0; i < ARRAY_LENGTH(dnd->items); i++) {
444                 x = (i % 4) * (item_width + item_padding) + item_padding;
445                 y = (i / 4) * (item_height + item_padding) + item_padding;
446                 if ((i ^ (i >> 2)) & 1)
447                         dnd->items[i] = item_create(display, x, y);
448                 else
449                         dnd->items[i] = NULL;
450         }
451
452         window_set_user_data(dnd->window, dnd);
453         window_set_redraw_handler(dnd->window, redraw_handler);
454         window_set_keyboard_focus_handler(dnd->window,
455                                           keyboard_focus_handler);
456         window_set_button_handler(dnd->window,
457                                   dnd_button_handler);
458
459         window_set_motion_handler(dnd->window,
460                                   dnd_motion_handler);
461
462         rectangle.width = 4 * (item_width + item_padding) + item_padding;
463         rectangle.height = 4 * (item_height + item_padding) + item_padding;
464         window_set_child_size(dnd->window, &rectangle);
465
466         display_add_drag_listener(display, &drag_listener, dnd);
467
468         dnd_draw(dnd);
469
470         return dnd;
471 }
472
473 static const GOptionEntry option_entries[] = {
474         { NULL }
475 };
476
477 int
478 main(int argc, char *argv[])
479 {
480         struct display *d;
481         struct dnd *dnd;
482         struct timeval tv;
483
484         gettimeofday(&tv, NULL);
485         srandom(tv.tv_usec);
486
487         d = display_create(&argc, &argv, option_entries);
488
489         dnd = dnd_create (d);
490
491         display_run(d);
492
493         return 0;
494 }