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