blob: 508870c43deed0ae61d97414bca7b4625cc6a5de [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
67struct frame {
68 int32_t width, height;
69 char *title;
70 uint32_t flags;
71 struct theme *theme;
72
73 struct {
74 int32_t x, y;
75 int32_t width, height;
76 } interior;
77 int shadow_margin;
78 int opaque_margin;
79 int geometry_dirty;
80
81 uint32_t status;
82
83 struct wl_list buttons;
84 struct wl_list pointers;
85};
86
87static struct frame_button *
88frame_button_create(struct frame *frame, const char *icon,
89 enum frame_status status_effect,
90 enum frame_button_flags flags)
91{
92 struct frame_button *button;
93
94 button = calloc(1, sizeof *button);
95 if (!button)
96 return NULL;
97
98 button->icon = cairo_image_surface_create_from_png(icon);
99 if (!button->icon) {
100 free(button);
101 return NULL;
102 }
103
104 button->frame = frame;
105 button->flags = flags;
106 button->status_effect = status_effect;
107
108 wl_list_insert(frame->buttons.prev, &button->link);
109
110 return button;
111}
112
113static void
114frame_button_destroy(struct frame_button *button)
115{
116 cairo_surface_destroy(button->icon);
117 free(button);
118}
119
120static void
121frame_button_enter(struct frame_button *button)
122{
123 if (!button->hover_count)
124 button->frame->status |= FRAME_STATUS_REPAINT;
125 button->hover_count++;
126}
127
128static void
129frame_button_leave(struct frame_button *button, struct frame_pointer *pointer)
130{
131 button->hover_count--;
132 if (!button->hover_count)
133 button->frame->status |= FRAME_STATUS_REPAINT;
134
135 /* In this case, we won't get a release */
136 if (pointer->active)
137 button->press_count--;
138}
139
140static void
141frame_button_press(struct frame_button *button)
142{
143 if (!button->press_count)
144 button->frame->status |= FRAME_STATUS_REPAINT;
145 button->press_count++;
146
147 if (button->flags & FRAME_BUTTON_CLICK_DOWN)
148 button->frame->status |= button->status_effect;
149}
150
151static void
152frame_button_release(struct frame_button *button)
153{
154 button->press_count--;
155 if (!button->press_count)
156 button->frame->status |= FRAME_STATUS_REPAINT;
157
158 if (!(button->flags & FRAME_BUTTON_CLICK_DOWN))
159 button->frame->status |= button->status_effect;
160}
161
162static void
163frame_button_repaint(struct frame_button *button, cairo_t *cr)
164{
165 int x, y;
166
167 if (!button->allocation.width)
168 return;
169 if (!button->allocation.height)
170 return;
171
172 x = button->allocation.x;
173 y = button->allocation.y;
174
175 cairo_save(cr);
176
177 if (button->flags & FRAME_BUTTON_DECORATED) {
178 cairo_set_line_width(cr, 1);
179
180 cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
181 cairo_rectangle(cr, x, y, 25, 16);
182
183 cairo_stroke_preserve(cr);
184
185 if (button->press_count) {
186 cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
187 } else if (button->hover_count) {
188 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
189 } else {
190 cairo_set_source_rgb(cr, 0.88, 0.88, 0.88);
191 }
192
193 cairo_fill (cr);
194
195 x += 4;
196 }
197
198 cairo_set_source_surface(cr, button->icon, x, y);
199 cairo_paint(cr);
200
201 cairo_restore(cr);
202}
203
204static struct frame_pointer *
205frame_pointer_get(struct frame *frame, void *data)
206{
207 struct frame_pointer *pointer;
208
209 wl_list_for_each(pointer, &frame->pointers, link)
210 if (pointer->data == data)
211 return pointer;
212
213 pointer = calloc(1, sizeof *pointer);
214 if (!pointer)
215 return NULL;
216
217 pointer->data = data;
218 wl_list_insert(&frame->pointers, &pointer->link);
219
220 return pointer;
221}
222
223static void
224frame_pointer_destroy(struct frame_pointer *pointer)
225{
226 wl_list_remove(&pointer->link);
227 free(pointer);
228}
229
230struct frame *
231frame_create(struct theme *t, int32_t width, int32_t height, uint32_t buttons,
232 const char *title)
233{
234 struct frame *frame;
235 struct frame_button *button;
236
237 frame = calloc(1, sizeof *frame);
238 if (!frame)
239 return NULL;
240
241 frame->width = width;
242 frame->height = height;
243 frame->flags = 0;
244 frame->theme = t;
245 frame->status = FRAME_STATUS_REPAINT;
246 frame->geometry_dirty = 1;
247
248 if (title) {
249 frame->title = strdup(title);
250 if (!frame->title)
251 goto free_frame;
252 }
253
254 wl_list_init(&frame->buttons);
255 wl_list_init(&frame->pointers);
256
257 button = frame_button_create(frame, DATADIR "/weston/icon_window.png",
258 FRAME_STATUS_MENU,
259 FRAME_BUTTON_CLICK_DOWN);
260 if (!button)
261 goto free_frame;
262
263 if (buttons & FRAME_BUTTON_CLOSE) {
264 button = frame_button_create(frame,
265 DATADIR "/weston/sign_close.png",
266 FRAME_STATUS_CLOSE,
267 FRAME_BUTTON_ALIGN_RIGHT |
268 FRAME_BUTTON_DECORATED);
269 if (!button)
270 goto free_frame;
271 }
272
273 if (buttons & FRAME_BUTTON_MAXIMIZE) {
274 button = frame_button_create(frame,
275 DATADIR "/weston/sign_maximize.png",
276 FRAME_STATUS_MAXIMIZE,
277 FRAME_BUTTON_ALIGN_RIGHT |
278 FRAME_BUTTON_DECORATED);
279 if (!button)
280 goto free_frame;
281 }
282
283 if (buttons & FRAME_BUTTON_MINIMIZE) {
284 button = frame_button_create(frame,
285 DATADIR "/weston/sign_minimize.png",
286 FRAME_STATUS_MINIMIZE,
287 FRAME_BUTTON_ALIGN_RIGHT |
288 FRAME_BUTTON_DECORATED);
289 if (!button)
290 goto free_frame;
291 }
292
293 return frame;
294
295free_frame:
296 free(frame->title);
297 free(frame);
298 return NULL;
299}
300
301void
302frame_destroy(struct frame *frame)
303{
304 struct frame_button *button, *next;
305
306 wl_list_for_each_safe(button, next, &frame->buttons, link)
307 frame_button_destroy(button);
308
309 free(frame->title);
310 free(frame);
311}
312
313int
314frame_set_title(struct frame *frame, const char *title)
315{
316 char *dup = NULL;
317
318 if (title) {
319 dup = strdup(title);
320 if (!dup)
321 return -1;
322 }
323
324 free(frame->title);
325 frame->title = dup;
326
327 frame->status |= FRAME_STATUS_REPAINT;
328
329 return 0;
330}
331
332void
333frame_set_flag(struct frame *frame, enum frame_flag flag)
334{
335 if (flag & FRAME_FLAG_MAXIMIZED && !(frame->flags & FRAME_FLAG_MAXIMIZED))
336 frame->geometry_dirty = 1;
337
338 frame->flags |= flag;
339 frame->status |= FRAME_STATUS_REPAINT;
340}
341
342void
343frame_unset_flag(struct frame *frame, enum frame_flag flag)
344{
345 if (flag & FRAME_FLAG_MAXIMIZED && frame->flags & FRAME_FLAG_MAXIMIZED)
346 frame->geometry_dirty = 1;
347
348 frame->flags &= ~flag;
349 frame->status |= FRAME_STATUS_REPAINT;
350}
351
352void
353frame_resize(struct frame *frame, int32_t width, int32_t height)
354{
355 frame->width = width;
356 frame->height = height;
357
358 frame->geometry_dirty = 1;
359 frame->status |= FRAME_STATUS_REPAINT;
360}
361
362void
363frame_resize_inside(struct frame *frame, int32_t width, int32_t height)
364{
365 struct theme *t = frame->theme;
366 int decoration_width, decoration_height;
367
368 if (frame->flags & FRAME_FLAG_MAXIMIZED) {
369 decoration_width = t->width * 2;
370 decoration_height = t->width + t->titlebar_height;
371 } else {
372 decoration_width = (t->width + t->margin) * 2;
373 decoration_height = t->width +
374 t->titlebar_height + t->margin * 2;
375 }
376
377 frame_resize(frame, width + decoration_width,
378 height + decoration_height);
379}
380
381int32_t
382frame_width(struct frame *frame)
383{
384 return frame->width;
385}
386
387int32_t
388frame_height(struct frame *frame)
389{
390 return frame->height;
391}
392
393static void
394frame_refresh_geometry(struct frame *frame)
395{
396 struct frame_button *button;
397 struct theme *t = frame->theme;
398 int x_l, x_r, y, w, h;
399 int32_t decoration_width, decoration_height;
400
401 if (!frame->geometry_dirty)
402 return;
403
404 if (frame->flags & FRAME_FLAG_MAXIMIZED) {
405 decoration_width = t->width * 2;
406 decoration_height = t->width + t->titlebar_height;
407
408 frame->interior.x = t->width;
409 frame->interior.y = t->titlebar_height;
410 frame->interior.width = frame->width - decoration_width;
411 frame->interior.height = frame->height - decoration_height;
412
413 frame->opaque_margin = 0;
414 frame->shadow_margin = 0;
415 } else {
416 decoration_width = (t->width + t->margin) * 2;
417 decoration_height = t->width +
418 t->titlebar_height + t->margin * 2;
419
420 frame->interior.x = t->width + t->margin;
421 frame->interior.y = t->titlebar_height + t->margin;
422 frame->interior.width = frame->width - decoration_width;
423 frame->interior.height = frame->height - decoration_height;
424
425 frame->opaque_margin = t->margin + t->frame_radius;
426 frame->shadow_margin = t->margin;
427 }
428
429 x_r = frame->width - t->width - frame->shadow_margin;
430 x_l = t->width + frame->shadow_margin;
431 y = t->width + frame->shadow_margin;
432 wl_list_for_each(button, &frame->buttons, link) {
433 const int button_padding = 4;
434 w = cairo_image_surface_get_width(button->icon);
435 h = cairo_image_surface_get_height(button->icon);
436
437 if (button->flags & FRAME_BUTTON_DECORATED)
438 w += 10;
439
440 if (button->flags & FRAME_BUTTON_ALIGN_RIGHT) {
441 x_r -= w;
442
443 button->allocation.x = x_r;
444 button->allocation.y = y;
445 button->allocation.width = w + 1;
446 button->allocation.height = h + 1;
447
448 x_r -= button_padding;
449 } else {
450 button->allocation.x = x_l;
451 button->allocation.y = y;
452 button->allocation.width = w + 1;
453 button->allocation.height = h + 1;
454
455 x_l += w;
456 x_l += button_padding;
457 }
458 }
459
460 frame->geometry_dirty = 0;
461}
462
463void
464frame_interior(struct frame *frame, int32_t *x, int32_t *y,
465 int32_t *width, int32_t *height)
466{
467 frame_refresh_geometry(frame);
468
469 if (x)
470 *x = frame->interior.x;
471 if (y)
472 *y = frame->interior.y;
473 if (width)
474 *width = frame->interior.width;
475 if (height)
476 *height = frame->interior.height;
477}
478
479void
480frame_input_rect(struct frame *frame, int32_t *x, int32_t *y,
481 int32_t *width, int32_t *height)
482{
483 frame_refresh_geometry(frame);
484
485 if (x)
486 *x = frame->shadow_margin;
487 if (y)
488 *y = frame->shadow_margin;
489 if (width)
490 *width = frame->width - frame->shadow_margin * 2;
491 if (height)
492 *height = frame->height - frame->shadow_margin * 2;
493}
494
495void
496frame_opaque_rect(struct frame *frame, int32_t *x, int32_t *y,
497 int32_t *width, int32_t *height)
498{
499 frame_refresh_geometry(frame);
500
501 if (x)
502 *x = frame->opaque_margin;
503 if (y)
504 *y = frame->opaque_margin;
505 if (width)
506 *width = frame->width - frame->opaque_margin * 2;
507 if (height)
508 *height = frame->height - frame->opaque_margin * 2;
509}
510
511uint32_t
512frame_status(struct frame *frame)
513{
514 return frame->status;
515}
516
517void
518frame_status_clear(struct frame *frame, enum frame_status status)
519{
520 frame->status &= ~status;
521}
522
523static struct frame_button *
524frame_find_button(struct frame *frame, int x, int y)
525{
526 struct frame_button *button;
527 int rel_x, rel_y;
528
529 wl_list_for_each(button, &frame->buttons, link) {
530 rel_x = x - button->allocation.x;
531 rel_y = y - button->allocation.y;
532
533 if (0 <= rel_x && rel_x < button->allocation.width &&
534 0 <= rel_y && rel_y < button->allocation.height)
535 return button;
536 }
537
538 return NULL;
539}
540
541enum theme_location
542frame_pointer_enter(struct frame *frame, void *data, int x, int y)
543{
544 return frame_pointer_motion(frame, data, x, y);
545}
546
547enum theme_location
548frame_pointer_motion(struct frame *frame, void *data, int x, int y)
549{
550 struct frame_pointer *pointer = frame_pointer_get(frame, data);
551 struct frame_button *button = frame_find_button(frame, x, y);
552 enum theme_location location;
553
554 location = theme_get_location(frame->theme, x, y,
555 frame->width, frame->height,
556 frame->flags & FRAME_FLAG_MAXIMIZED ?
557 THEME_FRAME_MAXIMIZED : 0);
558 if (!pointer)
559 return location;
560
561 pointer->x = x;
562 pointer->y = y;
563
564 if (pointer->hover_button == button)
565 return location;
566
567 if (pointer->hover_button)
568 frame_button_leave(pointer->hover_button, pointer);
569
570 /* No drags */
571 pointer->active = 0;
572 pointer->hover_button = button;
573
574 if (pointer->hover_button)
575 frame_button_enter(pointer->hover_button);
576
577 return location;
578}
579
580void
581frame_pointer_leave(struct frame *frame, void *data)
582{
583 struct frame_pointer *pointer = frame_pointer_get(frame, data);
584 if (!pointer)
585 return;
586
587 if (pointer->hover_button)
588 frame_button_leave(pointer->hover_button, pointer);
589
590 frame_pointer_destroy(pointer);
591}
592
593enum theme_location
594frame_pointer_button(struct frame *frame, void *data,
595 uint32_t button, enum frame_button_state state)
596{
597 struct frame_pointer *pointer = frame_pointer_get(frame, data);
598 enum theme_location location;
599
600 location = theme_get_location(frame->theme, pointer->x, pointer->y,
601 frame->width, frame->height,
602 frame->flags & FRAME_FLAG_MAXIMIZED ?
603 THEME_FRAME_MAXIMIZED : 0);
604
605 if (!pointer)
606 return location;
607
608 if (button == BTN_RIGHT) {
609 if (state == FRAME_BUTTON_PRESSED &&
610 location == THEME_LOCATION_TITLEBAR)
611 frame->status |= FRAME_STATUS_MENU;
612
613 } else if (button == BTN_LEFT && state == FRAME_BUTTON_PRESSED) {
614 if (pointer->hover_button) {
615 pointer->active = 1;
616 frame_button_press(pointer->hover_button);
617 return location;
618 } else {
619 switch (location) {
620 case THEME_LOCATION_TITLEBAR:
621 frame->status |= FRAME_STATUS_MOVE;
622 break;
623 case THEME_LOCATION_RESIZING_TOP:
624 case THEME_LOCATION_RESIZING_BOTTOM:
625 case THEME_LOCATION_RESIZING_LEFT:
626 case THEME_LOCATION_RESIZING_RIGHT:
627 case THEME_LOCATION_RESIZING_TOP_LEFT:
628 case THEME_LOCATION_RESIZING_TOP_RIGHT:
629 case THEME_LOCATION_RESIZING_BOTTOM_LEFT:
630 case THEME_LOCATION_RESIZING_BOTTOM_RIGHT:
631 frame->status |= FRAME_STATUS_RESIZE;
632 break;
633 default:
634 break;
635 }
636 }
637 } else if (button == BTN_LEFT && state == FRAME_BUTTON_RELEASED) {
638 if (pointer->hover_button && pointer->active)
639 frame_button_release(pointer->hover_button);
640
641 pointer->active = 0;
642 }
643
644 return location;
645}
646
647void
648frame_repaint(struct frame *frame, cairo_t *cr)
649{
650 struct frame_button *button;
651 uint32_t flags = 0;
652
653 frame_refresh_geometry(frame);
654
655 if (frame->flags & FRAME_FLAG_MAXIMIZED)
656 flags |= THEME_FRAME_MAXIMIZED;
657
658 if (frame->flags & FRAME_FLAG_ACTIVE)
659 flags |= THEME_FRAME_ACTIVE;
660
661 cairo_save(cr);
662 theme_render_frame(frame->theme, cr, frame->width, frame->height,
663 frame->title, flags);
664 cairo_restore(cr);
665
666 wl_list_for_each(button, &frame->buttons, link)
667 frame_button_repaint(button, cr);
668
669 frame_status_clear(frame, FRAME_STATUS_REPAINT);
670}