blob: fc8595076d0aac322e08f3a1c384885642a3e83d [file] [log] [blame]
Jason Ekstrand01c9ec32013-10-13 19:08:39 -05001/*
2 * Copyright © 2008 Kristian Høgsberg
3 * Copyright © 2012-2013 Collabora, Ltd.
4 * Copyright © 2013 Jason Ekstrand
5 *
6 * Permission to use, copy, modify, distribute, and sell this software and its
7 * documentation for any purpose is hereby granted without fee, provided that
8 * the above copyright notice appear in all copies and that both that copyright
9 * notice and this permission notice appear in supporting documentation, and
10 * that the name of the copyright holders not be used in advertising or
11 * publicity pertaining to distribution of the software without specific,
12 * written prior permission. The copyright holders make no representations
13 * about the suitability of this software for any purpose. It is provided "as
14 * is" without express or implied warranty.
15 *
16 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
20 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
21 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
22 * OF THIS SOFTWARE.
23 */
24
25#include "config.h"
26
27#include <stdlib.h>
28#include <string.h>
29#include <wayland-util.h>
30#include <linux/input.h>
31
32#include "cairo-util.h"
33
34enum frame_button_flags {
35 FRAME_BUTTON_ALIGN_RIGHT = 0x1,
36 FRAME_BUTTON_DECORATED = 0x2,
37 FRAME_BUTTON_CLICK_DOWN = 0x4,
38};
39
40struct frame_button {
41 struct frame *frame;
42 struct wl_list link; /* buttons_list */
43
44 cairo_surface_t *icon;
45 enum frame_button_flags flags;
46 int hover_count;
47 int press_count;
48
49 struct {
50 int x, y;
51 int width, height;
52 } allocation;
53
54 enum frame_status status_effect;
55};
56
57struct frame_pointer {
58 struct wl_list link;
59 void *data;
60
61 int x, y;
62
63 struct frame_button *hover_button;
64 int active;
65};
66
Jason Ekstrand3f66cf92013-10-13 19:08:40 -050067struct frame_touch {
68 struct wl_list link;
69 void *data;
70
71 int x, y;
72
73 struct frame_button *button;
74};
75
Jason Ekstrand01c9ec32013-10-13 19:08:39 -050076struct frame {
77 int32_t width, height;
78 char *title;
79 uint32_t flags;
80 struct theme *theme;
81
82 struct {
83 int32_t x, y;
84 int32_t width, height;
85 } interior;
86 int shadow_margin;
87 int opaque_margin;
88 int geometry_dirty;
89
90 uint32_t status;
91
92 struct wl_list buttons;
93 struct wl_list pointers;
Jason Ekstrand3f66cf92013-10-13 19:08:40 -050094 struct wl_list touches;
Jason Ekstrand01c9ec32013-10-13 19:08:39 -050095};
96
97static struct frame_button *
98frame_button_create(struct frame *frame, const char *icon,
99 enum frame_status status_effect,
100 enum frame_button_flags flags)
101{
102 struct frame_button *button;
103
104 button = calloc(1, sizeof *button);
105 if (!button)
106 return NULL;
107
108 button->icon = cairo_image_surface_create_from_png(icon);
109 if (!button->icon) {
110 free(button);
111 return NULL;
112 }
113
114 button->frame = frame;
115 button->flags = flags;
116 button->status_effect = status_effect;
117
118 wl_list_insert(frame->buttons.prev, &button->link);
119
120 return button;
121}
122
123static void
124frame_button_destroy(struct frame_button *button)
125{
126 cairo_surface_destroy(button->icon);
127 free(button);
128}
129
130static void
131frame_button_enter(struct frame_button *button)
132{
133 if (!button->hover_count)
134 button->frame->status |= FRAME_STATUS_REPAINT;
135 button->hover_count++;
136}
137
138static void
139frame_button_leave(struct frame_button *button, struct frame_pointer *pointer)
140{
141 button->hover_count--;
142 if (!button->hover_count)
143 button->frame->status |= FRAME_STATUS_REPAINT;
144
145 /* In this case, we won't get a release */
146 if (pointer->active)
147 button->press_count--;
148}
149
150static void
151frame_button_press(struct frame_button *button)
152{
153 if (!button->press_count)
154 button->frame->status |= FRAME_STATUS_REPAINT;
155 button->press_count++;
156
157 if (button->flags & FRAME_BUTTON_CLICK_DOWN)
158 button->frame->status |= button->status_effect;
159}
160
161static void
162frame_button_release(struct frame_button *button)
163{
164 button->press_count--;
165 if (!button->press_count)
166 button->frame->status |= FRAME_STATUS_REPAINT;
167
168 if (!(button->flags & FRAME_BUTTON_CLICK_DOWN))
169 button->frame->status |= button->status_effect;
170}
171
172static void
173frame_button_repaint(struct frame_button *button, cairo_t *cr)
174{
175 int x, y;
176
177 if (!button->allocation.width)
178 return;
179 if (!button->allocation.height)
180 return;
181
182 x = button->allocation.x;
183 y = button->allocation.y;
184
185 cairo_save(cr);
186
187 if (button->flags & FRAME_BUTTON_DECORATED) {
188 cairo_set_line_width(cr, 1);
189
190 cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
191 cairo_rectangle(cr, x, y, 25, 16);
192
193 cairo_stroke_preserve(cr);
194
195 if (button->press_count) {
196 cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
197 } else if (button->hover_count) {
198 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
199 } else {
200 cairo_set_source_rgb(cr, 0.88, 0.88, 0.88);
201 }
202
203 cairo_fill (cr);
204
205 x += 4;
206 }
207
208 cairo_set_source_surface(cr, button->icon, x, y);
209 cairo_paint(cr);
210
211 cairo_restore(cr);
212}
213
214static struct frame_pointer *
215frame_pointer_get(struct frame *frame, void *data)
216{
217 struct frame_pointer *pointer;
218
219 wl_list_for_each(pointer, &frame->pointers, link)
220 if (pointer->data == data)
221 return pointer;
222
223 pointer = calloc(1, sizeof *pointer);
224 if (!pointer)
225 return NULL;
226
227 pointer->data = data;
228 wl_list_insert(&frame->pointers, &pointer->link);
229
230 return pointer;
231}
232
233static void
234frame_pointer_destroy(struct frame_pointer *pointer)
235{
236 wl_list_remove(&pointer->link);
237 free(pointer);
238}
239
Jason Ekstrand3f66cf92013-10-13 19:08:40 -0500240static struct frame_touch *
241frame_touch_get(struct frame *frame, void *data)
242{
243 struct frame_touch *touch;
244
245 wl_list_for_each(touch, &frame->touches, link)
246 if (touch->data == data)
247 return touch;
248
249 touch = calloc(1, sizeof *touch);
250 if (!touch)
251 return NULL;
252
253 touch->data = data;
254 wl_list_insert(&frame->touches, &touch->link);
255
256 return touch;
257}
258
259static void
260frame_touch_destroy(struct frame_touch *touch)
261{
262 wl_list_remove(&touch->link);
263 free(touch);
264}
265
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500266struct frame *
267frame_create(struct theme *t, int32_t width, int32_t height, uint32_t buttons,
268 const char *title)
269{
270 struct frame *frame;
271 struct frame_button *button;
272
273 frame = calloc(1, sizeof *frame);
274 if (!frame)
275 return NULL;
276
277 frame->width = width;
278 frame->height = height;
279 frame->flags = 0;
280 frame->theme = t;
281 frame->status = FRAME_STATUS_REPAINT;
282 frame->geometry_dirty = 1;
283
284 if (title) {
285 frame->title = strdup(title);
286 if (!frame->title)
287 goto free_frame;
288 }
289
290 wl_list_init(&frame->buttons);
291 wl_list_init(&frame->pointers);
Jason Ekstrand3f66cf92013-10-13 19:08:40 -0500292 wl_list_init(&frame->touches);
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500293
294 button = frame_button_create(frame, DATADIR "/weston/icon_window.png",
295 FRAME_STATUS_MENU,
296 FRAME_BUTTON_CLICK_DOWN);
297 if (!button)
298 goto free_frame;
299
300 if (buttons & FRAME_BUTTON_CLOSE) {
301 button = frame_button_create(frame,
302 DATADIR "/weston/sign_close.png",
303 FRAME_STATUS_CLOSE,
304 FRAME_BUTTON_ALIGN_RIGHT |
305 FRAME_BUTTON_DECORATED);
306 if (!button)
307 goto free_frame;
308 }
309
310 if (buttons & FRAME_BUTTON_MAXIMIZE) {
311 button = frame_button_create(frame,
312 DATADIR "/weston/sign_maximize.png",
313 FRAME_STATUS_MAXIMIZE,
314 FRAME_BUTTON_ALIGN_RIGHT |
315 FRAME_BUTTON_DECORATED);
316 if (!button)
317 goto free_frame;
318 }
319
320 if (buttons & FRAME_BUTTON_MINIMIZE) {
321 button = frame_button_create(frame,
322 DATADIR "/weston/sign_minimize.png",
323 FRAME_STATUS_MINIMIZE,
324 FRAME_BUTTON_ALIGN_RIGHT |
325 FRAME_BUTTON_DECORATED);
326 if (!button)
327 goto free_frame;
328 }
329
330 return frame;
331
332free_frame:
333 free(frame->title);
334 free(frame);
335 return NULL;
336}
337
338void
339frame_destroy(struct frame *frame)
340{
341 struct frame_button *button, *next;
342
343 wl_list_for_each_safe(button, next, &frame->buttons, link)
344 frame_button_destroy(button);
345
346 free(frame->title);
347 free(frame);
348}
349
350int
351frame_set_title(struct frame *frame, const char *title)
352{
353 char *dup = NULL;
354
355 if (title) {
356 dup = strdup(title);
357 if (!dup)
358 return -1;
359 }
360
361 free(frame->title);
362 frame->title = dup;
363
364 frame->status |= FRAME_STATUS_REPAINT;
365
366 return 0;
367}
368
369void
370frame_set_flag(struct frame *frame, enum frame_flag flag)
371{
372 if (flag & FRAME_FLAG_MAXIMIZED && !(frame->flags & FRAME_FLAG_MAXIMIZED))
373 frame->geometry_dirty = 1;
374
375 frame->flags |= flag;
376 frame->status |= FRAME_STATUS_REPAINT;
377}
378
379void
380frame_unset_flag(struct frame *frame, enum frame_flag flag)
381{
382 if (flag & FRAME_FLAG_MAXIMIZED && frame->flags & FRAME_FLAG_MAXIMIZED)
383 frame->geometry_dirty = 1;
384
385 frame->flags &= ~flag;
386 frame->status |= FRAME_STATUS_REPAINT;
387}
388
389void
390frame_resize(struct frame *frame, int32_t width, int32_t height)
391{
392 frame->width = width;
393 frame->height = height;
394
395 frame->geometry_dirty = 1;
396 frame->status |= FRAME_STATUS_REPAINT;
397}
398
399void
400frame_resize_inside(struct frame *frame, int32_t width, int32_t height)
401{
402 struct theme *t = frame->theme;
403 int decoration_width, decoration_height;
404
405 if (frame->flags & FRAME_FLAG_MAXIMIZED) {
406 decoration_width = t->width * 2;
407 decoration_height = t->width + t->titlebar_height;
408 } else {
409 decoration_width = (t->width + t->margin) * 2;
410 decoration_height = t->width +
411 t->titlebar_height + t->margin * 2;
412 }
413
414 frame_resize(frame, width + decoration_width,
415 height + decoration_height);
416}
417
418int32_t
419frame_width(struct frame *frame)
420{
421 return frame->width;
422}
423
424int32_t
425frame_height(struct frame *frame)
426{
427 return frame->height;
428}
429
430static void
431frame_refresh_geometry(struct frame *frame)
432{
433 struct frame_button *button;
434 struct theme *t = frame->theme;
435 int x_l, x_r, y, w, h;
436 int32_t decoration_width, decoration_height;
437
438 if (!frame->geometry_dirty)
439 return;
440
441 if (frame->flags & FRAME_FLAG_MAXIMIZED) {
442 decoration_width = t->width * 2;
443 decoration_height = t->width + t->titlebar_height;
444
445 frame->interior.x = t->width;
446 frame->interior.y = t->titlebar_height;
447 frame->interior.width = frame->width - decoration_width;
448 frame->interior.height = frame->height - decoration_height;
449
450 frame->opaque_margin = 0;
451 frame->shadow_margin = 0;
452 } else {
453 decoration_width = (t->width + t->margin) * 2;
454 decoration_height = t->width +
455 t->titlebar_height + t->margin * 2;
456
457 frame->interior.x = t->width + t->margin;
458 frame->interior.y = t->titlebar_height + t->margin;
459 frame->interior.width = frame->width - decoration_width;
460 frame->interior.height = frame->height - decoration_height;
461
462 frame->opaque_margin = t->margin + t->frame_radius;
463 frame->shadow_margin = t->margin;
464 }
465
466 x_r = frame->width - t->width - frame->shadow_margin;
467 x_l = t->width + frame->shadow_margin;
468 y = t->width + frame->shadow_margin;
469 wl_list_for_each(button, &frame->buttons, link) {
470 const int button_padding = 4;
471 w = cairo_image_surface_get_width(button->icon);
472 h = cairo_image_surface_get_height(button->icon);
473
474 if (button->flags & FRAME_BUTTON_DECORATED)
475 w += 10;
476
477 if (button->flags & FRAME_BUTTON_ALIGN_RIGHT) {
478 x_r -= w;
479
480 button->allocation.x = x_r;
481 button->allocation.y = y;
482 button->allocation.width = w + 1;
483 button->allocation.height = h + 1;
484
485 x_r -= button_padding;
486 } else {
487 button->allocation.x = x_l;
488 button->allocation.y = y;
489 button->allocation.width = w + 1;
490 button->allocation.height = h + 1;
491
492 x_l += w;
493 x_l += button_padding;
494 }
495 }
496
497 frame->geometry_dirty = 0;
498}
499
500void
501frame_interior(struct frame *frame, int32_t *x, int32_t *y,
502 int32_t *width, int32_t *height)
503{
504 frame_refresh_geometry(frame);
505
506 if (x)
507 *x = frame->interior.x;
508 if (y)
509 *y = frame->interior.y;
510 if (width)
511 *width = frame->interior.width;
512 if (height)
513 *height = frame->interior.height;
514}
515
516void
517frame_input_rect(struct frame *frame, int32_t *x, int32_t *y,
518 int32_t *width, int32_t *height)
519{
520 frame_refresh_geometry(frame);
521
522 if (x)
523 *x = frame->shadow_margin;
524 if (y)
525 *y = frame->shadow_margin;
526 if (width)
527 *width = frame->width - frame->shadow_margin * 2;
528 if (height)
529 *height = frame->height - frame->shadow_margin * 2;
530}
531
532void
533frame_opaque_rect(struct frame *frame, int32_t *x, int32_t *y,
534 int32_t *width, int32_t *height)
535{
536 frame_refresh_geometry(frame);
537
538 if (x)
539 *x = frame->opaque_margin;
540 if (y)
541 *y = frame->opaque_margin;
542 if (width)
543 *width = frame->width - frame->opaque_margin * 2;
544 if (height)
545 *height = frame->height - frame->opaque_margin * 2;
546}
547
548uint32_t
549frame_status(struct frame *frame)
550{
551 return frame->status;
552}
553
554void
555frame_status_clear(struct frame *frame, enum frame_status status)
556{
557 frame->status &= ~status;
558}
559
560static struct frame_button *
561frame_find_button(struct frame *frame, int x, int y)
562{
563 struct frame_button *button;
564 int rel_x, rel_y;
565
566 wl_list_for_each(button, &frame->buttons, link) {
567 rel_x = x - button->allocation.x;
568 rel_y = y - button->allocation.y;
569
570 if (0 <= rel_x && rel_x < button->allocation.width &&
571 0 <= rel_y && rel_y < button->allocation.height)
572 return button;
573 }
574
575 return NULL;
576}
577
578enum theme_location
579frame_pointer_enter(struct frame *frame, void *data, int x, int y)
580{
581 return frame_pointer_motion(frame, data, x, y);
582}
583
584enum theme_location
585frame_pointer_motion(struct frame *frame, void *data, int x, int y)
586{
587 struct frame_pointer *pointer = frame_pointer_get(frame, data);
588 struct frame_button *button = frame_find_button(frame, x, y);
589 enum theme_location location;
590
591 location = theme_get_location(frame->theme, x, y,
592 frame->width, frame->height,
593 frame->flags & FRAME_FLAG_MAXIMIZED ?
594 THEME_FRAME_MAXIMIZED : 0);
595 if (!pointer)
596 return location;
597
598 pointer->x = x;
599 pointer->y = y;
600
601 if (pointer->hover_button == button)
602 return location;
603
604 if (pointer->hover_button)
605 frame_button_leave(pointer->hover_button, pointer);
606
607 /* No drags */
608 pointer->active = 0;
609 pointer->hover_button = button;
610
611 if (pointer->hover_button)
612 frame_button_enter(pointer->hover_button);
613
614 return location;
615}
616
617void
618frame_pointer_leave(struct frame *frame, void *data)
619{
620 struct frame_pointer *pointer = frame_pointer_get(frame, data);
621 if (!pointer)
622 return;
623
624 if (pointer->hover_button)
625 frame_button_leave(pointer->hover_button, pointer);
626
627 frame_pointer_destroy(pointer);
628}
629
630enum theme_location
631frame_pointer_button(struct frame *frame, void *data,
632 uint32_t button, enum frame_button_state state)
633{
634 struct frame_pointer *pointer = frame_pointer_get(frame, data);
635 enum theme_location location;
636
637 location = theme_get_location(frame->theme, pointer->x, pointer->y,
638 frame->width, frame->height,
639 frame->flags & FRAME_FLAG_MAXIMIZED ?
640 THEME_FRAME_MAXIMIZED : 0);
641
642 if (!pointer)
643 return location;
644
645 if (button == BTN_RIGHT) {
646 if (state == FRAME_BUTTON_PRESSED &&
647 location == THEME_LOCATION_TITLEBAR)
648 frame->status |= FRAME_STATUS_MENU;
649
650 } else if (button == BTN_LEFT && state == FRAME_BUTTON_PRESSED) {
651 if (pointer->hover_button) {
652 pointer->active = 1;
653 frame_button_press(pointer->hover_button);
654 return location;
655 } else {
656 switch (location) {
657 case THEME_LOCATION_TITLEBAR:
658 frame->status |= FRAME_STATUS_MOVE;
659 break;
660 case THEME_LOCATION_RESIZING_TOP:
661 case THEME_LOCATION_RESIZING_BOTTOM:
662 case THEME_LOCATION_RESIZING_LEFT:
663 case THEME_LOCATION_RESIZING_RIGHT:
664 case THEME_LOCATION_RESIZING_TOP_LEFT:
665 case THEME_LOCATION_RESIZING_TOP_RIGHT:
666 case THEME_LOCATION_RESIZING_BOTTOM_LEFT:
667 case THEME_LOCATION_RESIZING_BOTTOM_RIGHT:
668 frame->status |= FRAME_STATUS_RESIZE;
669 break;
670 default:
671 break;
672 }
673 }
674 } else if (button == BTN_LEFT && state == FRAME_BUTTON_RELEASED) {
675 if (pointer->hover_button && pointer->active)
676 frame_button_release(pointer->hover_button);
677
678 pointer->active = 0;
679 }
680
681 return location;
682}
683
684void
Jason Ekstrand3f66cf92013-10-13 19:08:40 -0500685frame_touch_down(struct frame *frame, void *data, int32_t id, int x, int y)
686{
687 struct frame_touch *touch = frame_touch_get(frame, data);
688 struct frame_button *button = frame_find_button(frame, x, y);
689 enum theme_location location;
690
691 if (id > 0)
692 return;
693
694 if (button) {
695 touch->button = button;
696 frame_button_press(touch->button);
697 return;
698 }
699
700 location = theme_get_location(frame->theme, x, y,
701 frame->width, frame->height,
702 frame->flags & FRAME_FLAG_MAXIMIZED ?
703 THEME_FRAME_MAXIMIZED : 0);
704
705 switch (location) {
706 case THEME_LOCATION_TITLEBAR:
707 frame->status |= FRAME_STATUS_MOVE;
708 break;
709 case THEME_LOCATION_RESIZING_TOP:
710 case THEME_LOCATION_RESIZING_BOTTOM:
711 case THEME_LOCATION_RESIZING_LEFT:
712 case THEME_LOCATION_RESIZING_RIGHT:
713 case THEME_LOCATION_RESIZING_TOP_LEFT:
714 case THEME_LOCATION_RESIZING_TOP_RIGHT:
715 case THEME_LOCATION_RESIZING_BOTTOM_LEFT:
716 case THEME_LOCATION_RESIZING_BOTTOM_RIGHT:
717 frame->status |= FRAME_STATUS_RESIZE;
718 break;
719 default:
720 break;
721 }
722}
723
724void
725frame_touch_up(struct frame *frame, void *data, int32_t id)
726{
727 struct frame_touch *touch = frame_touch_get(frame, data);
728
729 if (id > 0)
730 return;
731
732 if (touch->button) {
733 frame_button_release(touch->button);
734 frame_touch_destroy(touch);
735 return;
736 }
737}
738
739void
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500740frame_repaint(struct frame *frame, cairo_t *cr)
741{
742 struct frame_button *button;
743 uint32_t flags = 0;
744
745 frame_refresh_geometry(frame);
746
747 if (frame->flags & FRAME_FLAG_MAXIMIZED)
748 flags |= THEME_FRAME_MAXIMIZED;
749
750 if (frame->flags & FRAME_FLAG_ACTIVE)
751 flags |= THEME_FRAME_ACTIVE;
752
753 cairo_save(cr);
754 theme_render_frame(frame->theme, cr, frame->width, frame->height,
755 frame->title, flags);
756 cairo_restore(cr);
757
758 wl_list_for_each(button, &frame->buttons, link)
759 frame_button_repaint(button, cr);
760
761 frame_status_clear(frame, FRAME_STATUS_REPAINT);
762}