2 * Copyright © 2008 Kristian Høgsberg
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.
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
33 #include <cairo-drm.h>
35 #include <linux/input.h>
36 #include "wayland-client.h"
37 #include "wayland-glib.h"
42 struct wl_display *display;
43 struct wl_compositor *compositor;
44 struct wl_surface *surface;
46 struct rectangle allocation, saved_allocation, screen_allocation;
47 int minimum_width, minimum_height;
52 struct wl_input_device *grab_device;
54 cairo_drm_context_t *ctx;
56 cairo_surface_t *cairo_surface;
58 window_resize_handler_t resize_handler;
59 window_key_handler_t key_handler;
64 rounded_rect(cairo_t *cr, int x0, int y0, int x1, int y1, int radius)
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);
78 window_draw_decorations(struct window *window)
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;
87 window->cairo_surface =
88 cairo_drm_surface_create(window->ctx,
89 CAIRO_CONTENT_COLOR_ALPHA,
90 window->allocation.width,
91 window->allocation.height);
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);
97 cr = cairo_create(window->cairo_surface);
99 width = window->allocation.width - window->margin * 2;
100 height = window->allocation.height - window->margin * 2;
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);
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. */
114 cairo_surface_t *map;
116 map = cairo_drm_surface_map(window->cairo_surface);
118 cairo_drm_surface_unmap(window->cairo_surface, map);
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);
127 rounded_rect(cr, 2, 2, width - 2, height - 2, radius - 1);
128 cairo_set_source(cr, bright);
130 rounded_rect(cr, 3, 3, width - 2, height - 2, radius - 1);
131 cairo_set_source(cr, dim);
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);
140 cairo_pattern_destroy(gradient);
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);
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);
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);
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),
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);
189 window_draw_fullscreen(struct window *window)
191 struct wl_visual *visual;
193 window->cairo_surface =
194 cairo_drm_surface_create(window->ctx,
195 CAIRO_CONTENT_COLOR_ALPHA,
196 window->allocation.width,
197 window->allocation.height);
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),
207 wl_surface_map(window->surface,
208 window->allocation.x,
209 window->allocation.y,
210 window->allocation.width,
211 window->allocation.height);
215 window_draw(struct window *window)
217 if (window->fullscreen)
218 window_draw_fullscreen(window);
220 window_draw_decorations(window);
224 window_handle_acknowledge(void *data,
225 struct wl_compositor *compositor,
226 uint32_t key, uint32_t frame)
228 struct window *window = data;
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.. */
235 if (key == 0 && window->cairo_surface != NULL) {
236 cairo_surface_destroy(window->cairo_surface);
237 window->cairo_surface = NULL;
242 window_handle_frame(void *data,
243 struct wl_compositor *compositor,
244 uint32_t frame, uint32_t timestamp)
248 static const struct wl_compositor_listener compositor_listener = {
249 window_handle_acknowledge,
256 WINDOW_RESIZING_UPPER_LEFT,
257 WINDOW_RESIZING_UPPER_RIGHT,
258 WINDOW_RESIZING_LOWER_LEFT,
259 WINDOW_RESIZING_LOWER_RIGHT
265 LOCATION_UPPER_RIGHT,
267 LOCATION_LOWER_RIGHT,
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)
275 struct window *window = data;
277 switch (window->state) {
279 if (window->fullscreen)
281 if (window->grab_device != input_device)
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);
292 case WINDOW_RESIZING_LOWER_RIGHT:
293 if (window->fullscreen)
295 if (window->grab_device != input_device)
297 window->allocation.width = window->drag_x + x;
298 window->allocation.height = window->drag_y + y;
300 if (window->resize_handler)
301 (*window->resize_handler)(window,
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)
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;
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;
327 location = LOCATION_OUTSIDE;
330 if (button == BTN_LEFT && state == 1) {
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;
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;
345 window->state = WINDOW_STABLE;
348 } else if (button == BTN_LEFT &&
349 state == 0 && window->grab_device == input_device) {
350 window->state = WINDOW_STABLE;
355 window_handle_key(void *data, struct wl_input_device *input_device,
356 uint32_t button, uint32_t state)
358 struct window *window = data;
360 if (window->key_handler)
361 (*window->key_handler)(window, button, state,
365 static const struct wl_input_device_listener input_device_listener = {
366 window_handle_motion,
367 window_handle_button,
372 window_get_child_rectangle(struct window *window,
373 struct rectangle *rectangle)
375 if (window->fullscreen) {
376 *rectangle = window->allocation;
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;
386 window_set_child_size(struct window *window,
387 struct rectangle *rectangle)
389 if (!window->fullscreen) {
390 window->allocation.width = rectangle->width + 20 + window->margin * 2;
391 window->allocation.height = rectangle->height + 60 + window->margin * 2;
396 window_create_surface(struct window *window,
397 struct rectangle *rectangle)
399 return cairo_drm_surface_create(window->ctx,
400 CAIRO_CONTENT_COLOR_ALPHA,
406 window_copy(struct window *window,
407 struct rectangle *rectangle,
408 uint32_t name, uint32_t stride)
410 wl_surface_copy(window->surface,
420 window_copy_surface(struct window *window,
421 struct rectangle *rectangle,
422 cairo_surface_t *surface)
424 wl_surface_copy(window->surface,
427 cairo_drm_surface_get_name(surface),
428 cairo_drm_surface_get_stride(surface),
435 window_set_fullscreen(struct window *window, int fullscreen)
437 window->fullscreen = fullscreen;
438 if (window->fullscreen) {
439 window->saved_allocation = window->allocation;
440 window->allocation = window->screen_allocation;
442 window->allocation = window->saved_allocation;
447 window_set_resize_handler(struct window *window,
448 window_resize_handler_t handler, void *data)
450 window->resize_handler = handler;
451 window->user_data = data;
455 window_set_key_handler(struct window *window,
456 window_key_handler_t handler, void *data)
458 window->key_handler = handler;
459 window->user_data = data;
463 window_handle_geometry(void *data,
464 struct wl_output *output,
465 int32_t width, int32_t height)
467 struct window *window = data;
469 window->screen_allocation.x = 0;
470 window->screen_allocation.y = 0;
471 window->screen_allocation.width = width;
472 window->screen_allocation.height = height;
475 static const struct wl_output_listener output_listener = {
476 window_handle_geometry,
480 window_handle_global(struct wl_display *display,
481 struct wl_object *object, void *data)
483 struct window *window = data;
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;
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;
498 wl_input_device_add_listener(input_device,
499 &input_device_listener, window);
504 window_create(struct wl_display *display, int fd,
506 int32_t x, int32_t y, int32_t width, int32_t height)
508 struct window *window;
510 window = malloc(sizeof *window);
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;
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");
532 wl_display_add_global_listener(display,
533 window_handle_global, window);