blob: a53caa28c12eed63de556b2bff0f2a38b0d6a577 [file] [log] [blame]
Kristian Høgsbergb8cc24e2010-08-18 20:31:06 -04001/*
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
40static const char socket_name[] = "\0wayland";
41
42struct dnd {
43 struct window *window;
44 struct display *display;
45 uint32_t key;
46 struct item *items[16];
Kristian Høgsberg1d7ffd32010-08-25 16:34:05 -040047
Kristian Høgsberge968f9c2010-08-27 22:18:00 -040048 struct wl_buffer *translucent_buffer;
49 struct wl_buffer *opaque_buffer;
Kristian Høgsberg1d7ffd32010-08-25 16:34:05 -040050 int hotspot_x, hotspot_y;
Kristian Høgsberg4eb53602010-08-27 20:29:56 -040051 uint32_t tag;
52 const char *drag_type;
Kristian Høgsbergb8cc24e2010-08-18 20:31:06 -040053};
54
55struct item {
56 cairo_surface_t *surface;
57 int x, y;
58};
59
60static const int item_width = 64;
61static const int item_height = 64;
62static const int item_padding = 16;
63
64static struct item *
65item_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
142static void
143dnd_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
181static void
182redraw_handler(struct window *window, void *data)
183{
184 struct dnd *dnd = data;
185
186 dnd_draw(dnd);
187}
188
189static void
190keyboard_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
198static struct item *
199dnd_get_item(struct dnd *dnd, int32_t x, int32_t y)
200{
201 struct item *item;
Kristian Høgsberge968f9c2010-08-27 22:18:00 -0400202 struct rectangle rectangle;
Kristian Høgsbergb8cc24e2010-08-18 20:31:06 -0400203 int i;
204
Kristian Høgsberge968f9c2010-08-27 22:18:00 -0400205 window_get_child_rectangle(dnd->window, &rectangle);
206
207 x -= rectangle.x;
208 y -= rectangle.y;
209
Kristian Høgsbergb8cc24e2010-08-18 20:31:06 -0400210 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
221static void
Kristian Høgsberg506e20e2010-08-19 17:26:02 -0400222drag_handle_device(void *data,
223 struct wl_drag *drag, struct wl_input_device *device)
224{
225}
226
227static void
228drag_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{
Kristian Høgsberg4eb53602010-08-27 20:29:56 -0400233 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
Kristian Høgsberg506e20e2010-08-19 17:26:02 -0400239 fprintf(stderr, "drag pointer focus %p\n", surface);
240
Kristian Høgsberge968f9c2010-08-27 22:18:00 -0400241 if (!surface) {
242 dnd->drag_type = NULL;
243 return;
244 }
245
246 if (!dnd_get_item(dnd, surface_x, surface_y)) {
Kristian Høgsberg4eb53602010-08-27 20:29:56 -0400247 wl_drag_accept(drag, "text/plain");
248 dnd->drag_type = "text/plain";
249 } else {
Kristian Høgsberge968f9c2010-08-27 22:18:00 -0400250 wl_drag_accept(drag, NULL);
Kristian Høgsberg4eb53602010-08-27 20:29:56 -0400251 dnd->drag_type = NULL;
252 }
Kristian Høgsberg506e20e2010-08-19 17:26:02 -0400253}
254
255static void
256drag_offer(void *data,
257 struct wl_drag *drag, const char *type)
258{
259 fprintf(stderr, "drag offer %s\n", type);
260}
261
262static void
263drag_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{
Kristian Høgsberg4eb53602010-08-27 20:29:56 -0400268 struct dnd *dnd = data;
Kristian Høgsberg506e20e2010-08-19 17:26:02 -0400269
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. */
Kristian Høgsberge968f9c2010-08-27 22:18:00 -0400274 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 }
Kristian Høgsberg506e20e2010-08-19 17:26:02 -0400281}
282
283static void
284drag_target(void *data,
285 struct wl_drag *drag, const char *mime_type)
286{
Kristian Høgsberg1d7ffd32010-08-25 16:34:05 -0400287 struct dnd *dnd = data;
288 struct input *input;
289 struct wl_input_device *device;
290
Kristian Høgsberge968f9c2010-08-27 22:18:00 -0400291 fprintf(stderr, "target %s\n", mime_type);
Kristian Høgsberg1d7ffd32010-08-25 16:34:05 -0400292 input = wl_drag_get_user_data(drag);
293 device = input_get_input_device(input);
Kristian Høgsberge968f9c2010-08-27 22:18:00 -0400294 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);
Kristian Høgsberg506e20e2010-08-19 17:26:02 -0400300}
301
Kristian Høgsberg4eb53602010-08-27 20:29:56 -0400302static gboolean
303drop_io_func(GIOChannel *source, GIOCondition condition, gpointer data)
Kristian Høgsberg506e20e2010-08-19 17:26:02 -0400304{
Kristian Høgsberg4eb53602010-08-27 20:29:56 -0400305 struct dnd *dnd = data;
306 char buffer[256];
307 int fd;
308 unsigned int len;
309 GError *err = NULL;
Kristian Høgsberg506e20e2010-08-19 17:26:02 -0400310
Kristian Høgsberg4eb53602010-08-27 20:29:56 -0400311 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);
Kristian Høgsberg506e20e2010-08-19 17:26:02 -0400316
Kristian Høgsberg4eb53602010-08-27 20:29:56 -0400317 g_io_channel_unref(source);
318
319 return TRUE;
Kristian Høgsberg506e20e2010-08-19 17:26:02 -0400320}
321
322static void
Kristian Høgsberg4eb53602010-08-27 20:29:56 -0400323drag_drop(void *data, struct wl_drag *drag)
Kristian Høgsberg506e20e2010-08-19 17:26:02 -0400324{
Kristian Høgsberg4eb53602010-08-27 20:29:56 -0400325 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");
Kristian Høgsberge968f9c2010-08-27 22:18:00 -0400331 /* 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. */
Kristian Høgsberg4eb53602010-08-27 20:29:56 -0400335 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
348static void
349drag_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);
Kristian Høgsberg506e20e2010-08-19 17:26:02 -0400357}
358
359static const struct wl_drag_listener drag_listener = {
360 drag_handle_device,
361 drag_pointer_focus,
362 drag_offer,
363 drag_motion,
364 drag_target,
Kristian Høgsberg4eb53602010-08-27 20:29:56 -0400365 drag_drop,
366 drag_finish
Kristian Høgsberg506e20e2010-08-19 17:26:02 -0400367};
368
Kristian Høgsberge968f9c2010-08-27 22:18:00 -0400369static cairo_surface_t *
370create_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
Kristian Høgsberg506e20e2010-08-19 17:26:02 -0400418static void
Kristian Høgsbergb8cc24e2010-08-18 20:31:06 -0400419dnd_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;
Kristian Høgsberge968f9c2010-08-27 22:18:00 -0400424 int32_t x, y;
Kristian Høgsbergb8cc24e2010-08-18 20:31:06 -0400425 struct item *item;
Kristian Høgsberge968f9c2010-08-27 22:18:00 -0400426 cairo_surface_t *surface;
427 struct rectangle rectangle;
Kristian Høgsbergb8cc24e2010-08-18 20:31:06 -0400428
Kristian Høgsbergb8cc24e2010-08-18 20:31:06 -0400429 window_get_child_rectangle(dnd->window, &rectangle);
Kristian Høgsberge968f9c2010-08-27 22:18:00 -0400430 input_get_position(input, &x, &y);
431 item = dnd_get_item(dnd, x, y);
Kristian Høgsbergb8cc24e2010-08-18 20:31:06 -0400432 x -= rectangle.x;
433 y -= rectangle.y;
Kristian Høgsbergb8cc24e2010-08-18 20:31:06 -0400434
435 if (item && state == 1) {
436 fprintf(stderr, "start drag, item %p\n", item);
437
Kristian Høgsberge968f9c2010-08-27 22:18:00 -0400438 surface = create_drag_cursor(dnd, item, x, y, 1);
439 dnd->opaque_buffer =
440 display_get_buffer_for_surface(dnd->display, surface);
Kristian Høgsbergb8cc24e2010-08-18 20:31:06 -0400441
Kristian Høgsberge968f9c2010-08-27 22:18:00 -0400442 surface = create_drag_cursor(dnd, item, x, y, 0.2);
443 dnd->translucent_buffer =
444 display_get_buffer_for_surface(dnd->display, surface);
Kristian Høgsberg1d7ffd32010-08-25 16:34:05 -0400445
446 window_start_drag(window, input, time);
Kristian Høgsbergb8cc24e2010-08-18 20:31:06 -0400447
448 /* FIXME: We leak the surface because we can't free it
449 * until the server has referenced it. */
450 }
451}
452
453static int
454dnd_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;
Kristian Høgsbergb8cc24e2010-08-18 20:31:06 -0400461
Kristian Høgsberge968f9c2010-08-27 22:18:00 -0400462 item = dnd_get_item(dnd, sx, sy);
Kristian Høgsbergb8cc24e2010-08-18 20:31:06 -0400463
464 if (item)
465 return POINTER_HAND1;
466 else
467 return POINTER_LEFT_PTR;
468}
469
470static struct dnd *
471dnd_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
Kristian Høgsberg1d7ffd32010-08-25 16:34:05 -0400512 display_add_drag_listener(display, &drag_listener, dnd);
513
Kristian Høgsbergb8cc24e2010-08-18 20:31:06 -0400514 dnd_draw(dnd);
515
516 return dnd;
517}
518
519static const GOptionEntry option_entries[] = {
520 { NULL }
521};
522
523int
524main(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
Kristian Høgsbergb8cc24e2010-08-18 20:31:06 -0400535 dnd = dnd_create (d);
536
537 display_run(d);
538
539 return 0;
540}