Drop libdrm CFLAGS where no longer necessary.
[profile/ivi/wayland.git] / window.c
1 /*
2  * Copyright © 2008 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 <time.h>
31 #include <cairo.h>
32 #include <glib.h>
33 #include <cairo-drm.h>
34
35 #include <linux/input.h>
36 #include "wayland-client.h"
37 #include "wayland-glib.h"
38
39 #include "window.h"
40
41 struct window {
42         struct wl_display *display;
43         struct wl_compositor *compositor;
44         struct wl_surface *surface;
45         const char *title;
46         struct rectangle allocation, saved_allocation, screen_allocation;
47         int minimum_width, minimum_height;
48         int margin;
49         int drag_x, drag_y;
50         int state;
51         int fullscreen;
52         struct wl_input_device *grab_device;
53         uint32_t name;
54         cairo_drm_context_t *ctx;
55
56         cairo_surface_t *cairo_surface;
57
58         window_resize_handler_t resize_handler;
59         window_key_handler_t key_handler;
60         void *user_data;
61 };
62
63 static void
64 rounded_rect(cairo_t *cr, int x0, int y0, int x1, int y1, int radius)
65 {
66         cairo_move_to(cr, x0, y0 + radius);
67         cairo_arc(cr, x0 + radius, y0 + radius, radius, M_PI, 3 * M_PI / 2);
68         cairo_line_to(cr, x1 - radius, y0);
69         cairo_arc(cr, x1 - radius, y0 + radius, radius, 3 * M_PI / 2, 2 * M_PI);
70         cairo_line_to(cr, x1, y1 - radius);
71         cairo_arc(cr, x1 - radius, y1 - radius, radius, 0, M_PI / 2);
72         cairo_line_to(cr, x0 + radius, y1);
73         cairo_arc(cr, x0 + radius, y1 - radius, radius, M_PI / 2, M_PI);
74         cairo_close_path(cr);
75 }
76
77 static void
78 window_draw_decorations(struct window *window)
79 {
80         cairo_t *cr;
81         int border = 2, radius = 5;
82         cairo_text_extents_t extents;
83         cairo_pattern_t *gradient, *outline, *bright, *dim;
84         struct wl_visual *visual;
85         int width, height;
86
87         window->cairo_surface =
88                 cairo_drm_surface_create(window->ctx,
89                                          CAIRO_CONTENT_COLOR_ALPHA,
90                                          window->allocation.width,
91                                          window->allocation.height);
92
93         outline = cairo_pattern_create_rgb(0.1, 0.1, 0.1);
94         bright = cairo_pattern_create_rgb(0.8, 0.8, 0.8);
95         dim = cairo_pattern_create_rgb(0.4, 0.4, 0.4);
96
97         cr = cairo_create(window->cairo_surface);
98
99         width = window->allocation.width - window->margin * 2;
100         height = window->allocation.height - window->margin * 2;
101
102         cairo_translate(cr, window->margin + 7, window->margin + 5);
103         cairo_set_line_width (cr, border);
104         cairo_set_source_rgba(cr, 0, 0, 0, 0.7);
105         rounded_rect(cr, 0, 0, width, height, radius);
106         cairo_fill(cr);
107
108 #ifdef SLOW_BUT_PWETTY
109         /* FIXME: Aw, pretty drop shadows now have to fallback to sw.
110          * Ideally we should have convolution filters in cairo, but we
111          * can also fallback to compositing the shadow image a bunch
112          * of times according to the blur kernel. */
113         {
114                 cairo_surface_t *map;
115
116                 map = cairo_drm_surface_map(window->cairo_surface);
117                 blur_surface(map);
118                 cairo_drm_surface_unmap(window->cairo_surface, map);
119         }
120 #endif
121
122         cairo_translate(cr, -7, -5);
123         cairo_set_line_width (cr, border);
124         rounded_rect(cr, 1, 1, width - 1, height - 1, radius);
125         cairo_set_source(cr, outline);
126         cairo_stroke(cr);
127         rounded_rect(cr, 2, 2, width - 2, height - 2, radius - 1);
128         cairo_set_source(cr, bright);
129         cairo_stroke(cr);
130         rounded_rect(cr, 3, 3, width - 2, height - 2, radius - 1);
131         cairo_set_source(cr, dim);
132         cairo_stroke(cr);
133
134         rounded_rect(cr, 2, 2, width - 2, height - 2, radius - 1);
135         gradient = cairo_pattern_create_linear (0, 0, 0, 100);
136         cairo_pattern_add_color_stop_rgb(gradient, 0, 0.6, 0.6, 0.4);
137         cairo_pattern_add_color_stop_rgb(gradient, 1, 0.8, 0.8, 0.7);
138         cairo_set_source(cr, gradient);
139         cairo_fill(cr);
140         cairo_pattern_destroy(gradient);
141
142         cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
143         cairo_move_to(cr, 10, 50);
144         cairo_line_to(cr, width - 10, 50);
145         cairo_line_to(cr, width - 10, height - 10);
146         cairo_line_to(cr, 10, height - 10);
147         cairo_close_path(cr);
148         cairo_set_source(cr, dim);
149         cairo_stroke(cr);
150
151         cairo_move_to(cr, 11, 51);
152         cairo_line_to(cr, width - 10, 51);
153         cairo_line_to(cr, width - 10, height - 10);
154         cairo_line_to(cr, 11, height - 10);
155         cairo_close_path(cr);
156         cairo_set_source(cr, bright);
157         cairo_stroke(cr);
158
159         cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
160         cairo_set_font_size(cr, 14);
161         cairo_text_extents(cr, window->title, &extents);
162         cairo_move_to(cr, (width - extents.width) / 2, 10 - extents.y_bearing);
163         cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
164         cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
165         cairo_set_line_width (cr, 4);
166         cairo_text_path(cr, window->title);
167         cairo_set_source_rgb(cr, 0.1, 0.1, 0.1);
168         cairo_stroke_preserve(cr);
169         cairo_set_source_rgb(cr, 1, 1, 1);
170         cairo_fill(cr);
171         cairo_destroy(cr);
172
173         visual = wl_display_get_premultiplied_argb_visual(window->display);
174         wl_surface_attach(window->surface,
175                           cairo_drm_surface_get_name(window->cairo_surface),
176                           window->allocation.width,
177                           window->allocation.height,
178                           cairo_drm_surface_get_stride(window->cairo_surface),
179                           visual);
180
181         wl_surface_map(window->surface,
182                        window->allocation.x - window->margin,
183                        window->allocation.y - window->margin,
184                        window->allocation.width,
185                        window->allocation.height);
186 }
187
188 static void
189 window_draw_fullscreen(struct window *window)
190 {
191         struct wl_visual *visual;
192
193         window->cairo_surface =
194                 cairo_drm_surface_create(window->ctx,
195                                          CAIRO_CONTENT_COLOR_ALPHA,
196                                          window->allocation.width,
197                                          window->allocation.height);
198
199         visual = wl_display_get_premultiplied_argb_visual(window->display);
200         wl_surface_attach(window->surface,
201                           cairo_drm_surface_get_name(window->cairo_surface),
202                           window->allocation.width,
203                           window->allocation.height,
204                           cairo_drm_surface_get_stride(window->cairo_surface),
205                           visual);
206
207         wl_surface_map(window->surface,
208                        window->allocation.x,
209                        window->allocation.y,
210                        window->allocation.width,
211                        window->allocation.height);
212 }
213
214 void
215 window_draw(struct window *window)
216 {
217         if (window->fullscreen)
218                 window_draw_fullscreen(window);
219         else
220                 window_draw_decorations(window);
221 }
222
223 static void
224 window_handle_acknowledge(void *data,
225                           struct wl_compositor *compositor,
226                           uint32_t key, uint32_t frame)
227 {
228         struct window *window = data;
229
230         /* The acknowledge event means that the server
231          * processed our last commit request and we can now
232          * safely free the old window buffer if we resized and
233          * render the next frame into our back buffer.. */
234
235         if (key == 0 && window->cairo_surface != NULL) {
236                 cairo_surface_destroy(window->cairo_surface);
237                 window->cairo_surface = NULL;
238         }
239 }
240
241 static void
242 window_handle_frame(void *data,
243                     struct wl_compositor *compositor,
244                     uint32_t frame, uint32_t timestamp)
245 {
246 }
247
248 static const struct wl_compositor_listener compositor_listener = {
249         window_handle_acknowledge,
250         window_handle_frame,
251 };
252
253 enum window_state {
254         WINDOW_STABLE,
255         WINDOW_MOVING,
256         WINDOW_RESIZING_UPPER_LEFT,
257         WINDOW_RESIZING_UPPER_RIGHT,
258         WINDOW_RESIZING_LOWER_LEFT,
259         WINDOW_RESIZING_LOWER_RIGHT
260 };
261
262 enum location {
263         LOCATION_INTERIOR,
264         LOCATION_UPPER_LEFT,
265         LOCATION_UPPER_RIGHT,
266         LOCATION_LOWER_LEFT,
267         LOCATION_LOWER_RIGHT,
268         LOCATION_OUTSIDE
269 };
270
271 static void
272 window_handle_motion(void *data, struct wl_input_device *input_device,
273                      int32_t x, int32_t y, int32_t sx, int32_t sy)
274 {
275         struct window *window = data;
276
277         switch (window->state) {
278         case WINDOW_MOVING:
279                 if (window->fullscreen)
280                         break;
281                 if (window->grab_device != input_device)
282                         break;
283                 window->allocation.x = window->drag_x + x;
284                 window->allocation.y = window->drag_y + y;
285                 wl_surface_map(window->surface,
286                                window->allocation.x - window->margin,
287                                window->allocation.y - window->margin,
288                                window->allocation.width,
289                                window->allocation.height);
290                 wl_compositor_commit(window->compositor, 1);
291                 break;
292         case WINDOW_RESIZING_LOWER_RIGHT:
293                 if (window->fullscreen)
294                         break;
295                 if (window->grab_device != input_device)
296                         break;
297                 window->allocation.width = window->drag_x + x;
298                 window->allocation.height = window->drag_y + y;
299
300                 if (window->resize_handler)
301                         (*window->resize_handler)(window,
302                                                   window->user_data);
303
304                 break;
305         }
306 }
307
308 static void window_handle_button(void *data, struct wl_input_device *input_device,
309                                  uint32_t button, uint32_t state,
310                                  int32_t x, int32_t y, int32_t sx, int32_t sy)
311 {
312         struct window *window = data;
313         int32_t left = window->allocation.x;
314         int32_t right = window->allocation.x +
315                 window->allocation.width - window->margin * 2;
316         int32_t top = window->allocation.y;
317         int32_t bottom = window->allocation.y +
318                 window->allocation.height - window->margin * 2;
319         int grip_size = 16, location;
320         
321         if (right - grip_size <= x && x < right &&
322             bottom - grip_size <= y && y < bottom) {
323                 location = LOCATION_LOWER_RIGHT;
324         } else if (left <= x && x < right && top <= y && y < bottom) {
325                 location = LOCATION_INTERIOR;
326         } else {
327                 location = LOCATION_OUTSIDE;
328         }
329
330         if (button == BTN_LEFT && state == 1) {
331                 switch (location) {
332                 case LOCATION_INTERIOR:
333                         window->drag_x = window->allocation.x - x;
334                         window->drag_y = window->allocation.y - y;
335                         window->state = WINDOW_MOVING;
336                         window->grab_device = input_device;
337                         break;
338                 case LOCATION_LOWER_RIGHT:
339                         window->drag_x = window->allocation.width - x;
340                         window->drag_y = window->allocation.height - y;
341                         window->state = WINDOW_RESIZING_LOWER_RIGHT;
342                         window->grab_device = input_device;
343                         break;
344                 default:
345                         window->state = WINDOW_STABLE;
346                         break;
347                 }
348         } else if (button == BTN_LEFT &&
349                    state == 0 && window->grab_device == input_device) {
350                 window->state = WINDOW_STABLE;
351         }
352 }
353
354 static void
355 window_handle_key(void *data, struct wl_input_device *input_device,
356                   uint32_t button, uint32_t state)
357 {
358         struct window *window = data;
359
360         if (window->key_handler)
361                 (*window->key_handler)(window, button, state,
362                                        window->user_data);
363 }
364
365 static const struct wl_input_device_listener input_device_listener = {
366         window_handle_motion,
367         window_handle_button,
368         window_handle_key,
369 };
370
371 void
372 window_get_child_rectangle(struct window *window,
373                            struct rectangle *rectangle)
374 {
375         if (window->fullscreen) {
376                 *rectangle = window->allocation;
377         } else {
378                 rectangle->x = window->margin + 10;
379                 rectangle->y = window->margin + 50;
380                 rectangle->width = window->allocation.width - 20 - window->margin * 2;
381                 rectangle->height = window->allocation.height - 60 - window->margin * 2;
382         }
383 }
384
385 void
386 window_set_child_size(struct window *window,
387                       struct rectangle *rectangle)
388 {
389         if (!window->fullscreen) {
390                 window->allocation.width = rectangle->width + 20 + window->margin * 2;
391                 window->allocation.height = rectangle->height + 60 + window->margin * 2;
392         }
393 }
394
395 cairo_surface_t *
396 window_create_surface(struct window *window,
397                       struct rectangle *rectangle)
398 {
399         return cairo_drm_surface_create(window->ctx,
400                                         CAIRO_CONTENT_COLOR_ALPHA,
401                                         rectangle->width,
402                                         rectangle->height);
403 }
404
405 void
406 window_copy(struct window *window,
407             struct rectangle *rectangle,
408             uint32_t name, uint32_t stride)
409 {
410         wl_surface_copy(window->surface,
411                         rectangle->x,
412                         rectangle->y,
413                         name, stride,
414                         0, 0,
415                         rectangle->width,
416                         rectangle->height);
417 }
418
419 void
420 window_copy_surface(struct window *window,
421                     struct rectangle *rectangle,
422                     cairo_surface_t *surface)
423 {
424         wl_surface_copy(window->surface,
425                         rectangle->x,
426                         rectangle->y,
427                         cairo_drm_surface_get_name(surface),
428                         cairo_drm_surface_get_stride(surface),
429                         0, 0,
430                         rectangle->width,
431                         rectangle->height);
432 }
433
434 void
435 window_set_fullscreen(struct window *window, int fullscreen)
436 {
437         window->fullscreen = fullscreen;
438         if (window->fullscreen) {
439                 window->saved_allocation = window->allocation;
440                 window->allocation = window->screen_allocation;
441         } else {
442                 window->allocation = window->saved_allocation;
443         }
444 }
445
446 void
447 window_set_resize_handler(struct window *window,
448                           window_resize_handler_t handler, void *data)
449 {
450         window->resize_handler = handler;
451         window->user_data = data;
452 }
453
454 void
455 window_set_key_handler(struct window *window,
456                        window_key_handler_t handler, void *data)
457 {
458         window->key_handler = handler;
459         window->user_data = data;
460 }
461
462 static void
463 window_handle_geometry(void *data,
464                        struct wl_output *output,
465                        int32_t width, int32_t height)
466 {
467         struct window *window = data;
468
469         window->screen_allocation.x = 0;
470         window->screen_allocation.y = 0;
471         window->screen_allocation.width = width;
472         window->screen_allocation.height = height;
473 }
474
475 static const struct wl_output_listener output_listener = {
476         window_handle_geometry,
477 };
478
479 static void
480 window_handle_global(struct wl_display *display,
481                      struct wl_object *object, void *data)
482 {
483         struct window *window = data;
484
485         if (wl_object_implements(object, "compositor", 1)) { 
486                 window->compositor = (struct wl_compositor *) object;
487                 wl_compositor_add_listener(window->compositor,
488                                            &compositor_listener, window);
489         } else if (wl_object_implements(object, "output", 1)) {
490                 struct wl_output *output = (struct wl_output *) object;
491
492                 wl_output_add_listener(output,
493                                        &output_listener, window);
494         } else if (wl_object_implements(object, "input_device", 1)) {
495                 struct wl_input_device *input_device =
496                         (struct wl_input_device *) object;
497
498                 wl_input_device_add_listener(input_device,
499                                              &input_device_listener, window);
500         }
501 }
502
503 struct window *
504 window_create(struct wl_display *display, int fd,
505               const char *title,
506               int32_t x, int32_t y, int32_t width, int32_t height)
507 {
508         struct window *window;
509
510         window = malloc(sizeof *window);
511         if (window == NULL)
512                 return NULL;
513
514         memset(window, 0, sizeof *window);
515         window->display = display;
516         window->title = strdup(title);
517         window->compositor = wl_display_get_compositor(display);
518         window->surface = wl_compositor_create_surface(window->compositor);
519         window->allocation.x = x;
520         window->allocation.y = y;
521         window->allocation.width = width;
522         window->allocation.height = height;
523         window->saved_allocation = window->allocation;
524         window->margin = 16;
525         window->state = WINDOW_STABLE;
526         window->ctx = cairo_drm_context_get_for_fd(fd);
527         if (window->ctx == NULL) {
528                 fprintf(stderr, "failed to get cairo drm context\n");
529                 return NULL;
530         }
531
532         wl_display_add_global_listener(display,
533                                        window_handle_global, window);
534
535         return window;
536 }