blob: acac2ca88ca473d1bc4c2147b596905d26e7ebca [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 *
Bryce Harrington6c6164c2015-06-11 14:20:17 -07006 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
Jason Ekstrand01c9ec32013-10-13 19:08:39 -050013 *
Bryce Harrington6c6164c2015-06-11 14:20:17 -070014 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial
16 * portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
22 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
23 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 * SOFTWARE.
Jason Ekstrand01c9ec32013-10-13 19:08:39 -050026 */
27
28#include "config.h"
29
Jussi Kukkonen649bbce2016-07-19 14:16:27 +030030#include <stdint.h>
Jason Ekstrand01c9ec32013-10-13 19:08:39 -050031#include <stdlib.h>
32#include <string.h>
33#include <wayland-util.h>
34#include <linux/input.h>
35
36#include "cairo-util.h"
Derek Foremane2772762018-02-06 15:18:38 -060037#include "shared/file-util.h"
Jason Ekstrand01c9ec32013-10-13 19:08:39 -050038
39enum frame_button_flags {
40 FRAME_BUTTON_ALIGN_RIGHT = 0x1,
41 FRAME_BUTTON_DECORATED = 0x2,
42 FRAME_BUTTON_CLICK_DOWN = 0x4,
43};
44
45struct frame_button {
46 struct frame *frame;
47 struct wl_list link; /* buttons_list */
48
49 cairo_surface_t *icon;
50 enum frame_button_flags flags;
51 int hover_count;
52 int press_count;
53
54 struct {
55 int x, y;
56 int width, height;
57 } allocation;
58
59 enum frame_status status_effect;
60};
61
Jason Ekstrand0bdd58f2013-10-27 22:25:03 -050062struct frame_pointer_button {
63 struct wl_list link;
64 uint32_t button;
65 enum theme_location press_location;
66 struct frame_button *frame_button;
67};
68
Jason Ekstrand01c9ec32013-10-13 19:08:39 -050069struct frame_pointer {
70 struct wl_list link;
71 void *data;
72
73 int x, y;
74
75 struct frame_button *hover_button;
Jason Ekstrand0bdd58f2013-10-27 22:25:03 -050076 struct wl_list down_buttons;
Jason Ekstrand01c9ec32013-10-13 19:08:39 -050077};
78
Jason Ekstrand3f66cf92013-10-13 19:08:40 -050079struct frame_touch {
80 struct wl_list link;
81 void *data;
82
83 int x, y;
84
85 struct frame_button *button;
86};
87
Jason Ekstrand01c9ec32013-10-13 19:08:39 -050088struct frame {
89 int32_t width, height;
90 char *title;
91 uint32_t flags;
92 struct theme *theme;
93
94 struct {
95 int32_t x, y;
96 int32_t width, height;
97 } interior;
98 int shadow_margin;
99 int opaque_margin;
100 int geometry_dirty;
101
Louis-Francis Ratté-Boulianne864e39b2017-11-13 16:20:55 -0500102 cairo_rectangle_int_t title_rect;
103
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500104 uint32_t status;
105
106 struct wl_list buttons;
107 struct wl_list pointers;
Jason Ekstrand3f66cf92013-10-13 19:08:40 -0500108 struct wl_list touches;
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500109};
110
111static struct frame_button *
Emmanuel Gil Peyrot6b58ea82017-12-01 19:20:40 +0100112frame_button_create_from_surface(struct frame *frame, cairo_surface_t *icon,
113 enum frame_status status_effect,
114 enum frame_button_flags flags)
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500115{
116 struct frame_button *button;
117
118 button = calloc(1, sizeof *button);
119 if (!button)
120 return NULL;
121
Emmanuel Gil Peyrot6b58ea82017-12-01 19:20:40 +0100122 button->icon = icon;
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500123 button->frame = frame;
124 button->flags = flags;
125 button->status_effect = status_effect;
126
127 wl_list_insert(frame->buttons.prev, &button->link);
128
129 return button;
130}
131
Emmanuel Gil Peyrot6b58ea82017-12-01 19:20:40 +0100132static struct frame_button *
133frame_button_create(struct frame *frame, const char *icon_name,
134 enum frame_status status_effect,
135 enum frame_button_flags flags)
136{
137 struct frame_button *button;
138 cairo_surface_t *icon;
139
140 icon = cairo_image_surface_create_from_png(icon_name);
141 if (cairo_surface_status(icon) != CAIRO_STATUS_SUCCESS)
142 goto error;
143
144 button = frame_button_create_from_surface(frame, icon, status_effect,
145 flags);
146 if (!button)
147 goto error;
148
149 return button;
150
151error:
152 cairo_surface_destroy(icon);
153 return NULL;
154}
155
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500156static void
157frame_button_destroy(struct frame_button *button)
158{
159 cairo_surface_destroy(button->icon);
160 free(button);
161}
162
163static void
164frame_button_enter(struct frame_button *button)
165{
166 if (!button->hover_count)
167 button->frame->status |= FRAME_STATUS_REPAINT;
168 button->hover_count++;
169}
170
171static void
172frame_button_leave(struct frame_button *button, struct frame_pointer *pointer)
173{
174 button->hover_count--;
175 if (!button->hover_count)
176 button->frame->status |= FRAME_STATUS_REPAINT;
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500177}
178
179static void
180frame_button_press(struct frame_button *button)
181{
182 if (!button->press_count)
183 button->frame->status |= FRAME_STATUS_REPAINT;
184 button->press_count++;
185
186 if (button->flags & FRAME_BUTTON_CLICK_DOWN)
187 button->frame->status |= button->status_effect;
188}
189
190static void
191frame_button_release(struct frame_button *button)
192{
193 button->press_count--;
Jason Ekstrand0bdd58f2013-10-27 22:25:03 -0500194 if (button->press_count)
195 return;
196
197 button->frame->status |= FRAME_STATUS_REPAINT;
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500198
199 if (!(button->flags & FRAME_BUTTON_CLICK_DOWN))
200 button->frame->status |= button->status_effect;
201}
202
203static void
Jason Ekstrand0bdd58f2013-10-27 22:25:03 -0500204frame_button_cancel(struct frame_button *button)
205{
206 button->press_count--;
207 if (!button->press_count)
208 button->frame->status |= FRAME_STATUS_REPAINT;
209}
210
211static void
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500212frame_button_repaint(struct frame_button *button, cairo_t *cr)
213{
214 int x, y;
215
216 if (!button->allocation.width)
217 return;
218 if (!button->allocation.height)
219 return;
220
221 x = button->allocation.x;
222 y = button->allocation.y;
223
224 cairo_save(cr);
225
226 if (button->flags & FRAME_BUTTON_DECORATED) {
227 cairo_set_line_width(cr, 1);
228
229 cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
230 cairo_rectangle(cr, x, y, 25, 16);
231
232 cairo_stroke_preserve(cr);
233
234 if (button->press_count) {
235 cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
236 } else if (button->hover_count) {
237 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
238 } else {
239 cairo_set_source_rgb(cr, 0.88, 0.88, 0.88);
240 }
241
242 cairo_fill (cr);
243
244 x += 4;
245 }
246
247 cairo_set_source_surface(cr, button->icon, x, y);
248 cairo_paint(cr);
249
250 cairo_restore(cr);
251}
252
253static struct frame_pointer *
254frame_pointer_get(struct frame *frame, void *data)
255{
256 struct frame_pointer *pointer;
257
258 wl_list_for_each(pointer, &frame->pointers, link)
259 if (pointer->data == data)
260 return pointer;
261
262 pointer = calloc(1, sizeof *pointer);
263 if (!pointer)
264 return NULL;
265
266 pointer->data = data;
Jason Ekstrand0bdd58f2013-10-27 22:25:03 -0500267 wl_list_init(&pointer->down_buttons);
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500268 wl_list_insert(&frame->pointers, &pointer->link);
269
270 return pointer;
271}
272
273static void
274frame_pointer_destroy(struct frame_pointer *pointer)
275{
276 wl_list_remove(&pointer->link);
277 free(pointer);
278}
279
Jason Ekstrand3f66cf92013-10-13 19:08:40 -0500280static struct frame_touch *
281frame_touch_get(struct frame *frame, void *data)
282{
283 struct frame_touch *touch;
284
285 wl_list_for_each(touch, &frame->touches, link)
286 if (touch->data == data)
287 return touch;
288
289 touch = calloc(1, sizeof *touch);
290 if (!touch)
291 return NULL;
292
293 touch->data = data;
294 wl_list_insert(&frame->touches, &touch->link);
295
296 return touch;
297}
298
299static void
300frame_touch_destroy(struct frame_touch *touch)
301{
302 wl_list_remove(&touch->link);
303 free(touch);
304}
305
U. Artie Eoff107de962014-01-17 11:19:46 -0800306void
307frame_destroy(struct frame *frame)
308{
309 struct frame_button *button, *next;
310 struct frame_touch *touch, *next_touch;
311 struct frame_pointer *pointer, *next_pointer;
312
313 wl_list_for_each_safe(button, next, &frame->buttons, link)
314 frame_button_destroy(button);
315
316 wl_list_for_each_safe(touch, next_touch, &frame->touches, link)
317 frame_touch_destroy(touch);
318
319 wl_list_for_each_safe(pointer, next_pointer, &frame->pointers, link)
320 frame_pointer_destroy(pointer);
321
322 free(frame->title);
323 free(frame);
324}
325
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500326struct frame *
327frame_create(struct theme *t, int32_t width, int32_t height, uint32_t buttons,
Emmanuel Gil Peyrot6b58ea82017-12-01 19:20:40 +0100328 const char *title, cairo_surface_t *icon)
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500329{
330 struct frame *frame;
331 struct frame_button *button;
332
333 frame = calloc(1, sizeof *frame);
334 if (!frame)
335 return NULL;
336
337 frame->width = width;
338 frame->height = height;
339 frame->flags = 0;
340 frame->theme = t;
341 frame->status = FRAME_STATUS_REPAINT;
342 frame->geometry_dirty = 1;
343
U. Artie Eoff107de962014-01-17 11:19:46 -0800344 wl_list_init(&frame->buttons);
345 wl_list_init(&frame->pointers);
346 wl_list_init(&frame->touches);
347
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500348 if (title) {
349 frame->title = strdup(title);
350 if (!frame->title)
351 goto free_frame;
352 }
353
Kristian Høgsberg89f4bc42013-10-23 22:12:13 -0700354 if (title) {
Emmanuel Gil Peyrot6b58ea82017-12-01 19:20:40 +0100355 if (icon) {
356 button = frame_button_create_from_surface(frame,
357 icon,
358 FRAME_STATUS_MENU,
359 FRAME_BUTTON_CLICK_DOWN);
360 } else {
Derek Foremane2772762018-02-06 15:18:38 -0600361 char *name = file_name_with_datadir("icon_window.png");
362
363 if (!name)
364 goto free_frame;
365
Emmanuel Gil Peyrot6b58ea82017-12-01 19:20:40 +0100366 button = frame_button_create(frame,
Derek Foremane2772762018-02-06 15:18:38 -0600367 name,
Emmanuel Gil Peyrot6b58ea82017-12-01 19:20:40 +0100368 FRAME_STATUS_MENU,
369 FRAME_BUTTON_CLICK_DOWN);
Derek Foremane2772762018-02-06 15:18:38 -0600370 free(name);
Emmanuel Gil Peyrot6b58ea82017-12-01 19:20:40 +0100371 }
Kristian Høgsberg89f4bc42013-10-23 22:12:13 -0700372 if (!button)
373 goto free_frame;
374 }
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500375
376 if (buttons & FRAME_BUTTON_CLOSE) {
Derek Foremane2772762018-02-06 15:18:38 -0600377 char *name = file_name_with_datadir("sign_close.png");
378
379 if (!name)
380 goto free_frame;
381
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500382 button = frame_button_create(frame,
Derek Foremane2772762018-02-06 15:18:38 -0600383 name,
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500384 FRAME_STATUS_CLOSE,
385 FRAME_BUTTON_ALIGN_RIGHT |
386 FRAME_BUTTON_DECORATED);
Derek Foremane2772762018-02-06 15:18:38 -0600387 free(name);
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500388 if (!button)
389 goto free_frame;
390 }
391
392 if (buttons & FRAME_BUTTON_MAXIMIZE) {
Derek Foremane2772762018-02-06 15:18:38 -0600393 char *name = file_name_with_datadir("sign_maximize.png");
394
395 if (!name)
396 goto free_frame;
397
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500398 button = frame_button_create(frame,
Derek Foremane2772762018-02-06 15:18:38 -0600399 name,
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500400 FRAME_STATUS_MAXIMIZE,
401 FRAME_BUTTON_ALIGN_RIGHT |
402 FRAME_BUTTON_DECORATED);
Derek Foremane2772762018-02-06 15:18:38 -0600403 free(name);
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500404 if (!button)
405 goto free_frame;
406 }
407
408 if (buttons & FRAME_BUTTON_MINIMIZE) {
Derek Foremane2772762018-02-06 15:18:38 -0600409 char *name = file_name_with_datadir("sign_minimize.png");
410
411 if (!name)
412 goto free_frame;
413
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500414 button = frame_button_create(frame,
Derek Foremane2772762018-02-06 15:18:38 -0600415 name,
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500416 FRAME_STATUS_MINIMIZE,
417 FRAME_BUTTON_ALIGN_RIGHT |
418 FRAME_BUTTON_DECORATED);
Derek Foremane2772762018-02-06 15:18:38 -0600419 free(name);
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500420 if (!button)
421 goto free_frame;
422 }
423
424 return frame;
425
426free_frame:
U. Artie Eoff107de962014-01-17 11:19:46 -0800427 frame_destroy(frame);
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500428 return NULL;
429}
430
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500431int
432frame_set_title(struct frame *frame, const char *title)
433{
434 char *dup = NULL;
435
436 if (title) {
437 dup = strdup(title);
438 if (!dup)
439 return -1;
440 }
441
442 free(frame->title);
443 frame->title = dup;
444
Boyan Ding9c5aedf2014-07-04 15:19:23 +0800445 frame->geometry_dirty = 1;
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500446 frame->status |= FRAME_STATUS_REPAINT;
447
448 return 0;
449}
450
451void
452frame_set_flag(struct frame *frame, enum frame_flag flag)
453{
454 if (flag & FRAME_FLAG_MAXIMIZED && !(frame->flags & FRAME_FLAG_MAXIMIZED))
455 frame->geometry_dirty = 1;
456
457 frame->flags |= flag;
458 frame->status |= FRAME_STATUS_REPAINT;
459}
460
461void
462frame_unset_flag(struct frame *frame, enum frame_flag flag)
463{
464 if (flag & FRAME_FLAG_MAXIMIZED && frame->flags & FRAME_FLAG_MAXIMIZED)
465 frame->geometry_dirty = 1;
466
467 frame->flags &= ~flag;
468 frame->status |= FRAME_STATUS_REPAINT;
469}
470
471void
472frame_resize(struct frame *frame, int32_t width, int32_t height)
473{
474 frame->width = width;
475 frame->height = height;
476
477 frame->geometry_dirty = 1;
478 frame->status |= FRAME_STATUS_REPAINT;
479}
480
481void
482frame_resize_inside(struct frame *frame, int32_t width, int32_t height)
483{
484 struct theme *t = frame->theme;
Kristian Høgsberg89f4bc42013-10-23 22:12:13 -0700485 int decoration_width, decoration_height, titlebar_height;
486
Boyan Dingc4902122014-07-04 15:19:22 +0800487 if (frame->title || !wl_list_empty(&frame->buttons))
Kristian Høgsberg89f4bc42013-10-23 22:12:13 -0700488 titlebar_height = t->titlebar_height;
489 else
490 titlebar_height = t->width;
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500491
492 if (frame->flags & FRAME_FLAG_MAXIMIZED) {
493 decoration_width = t->width * 2;
Kristian Høgsberg89f4bc42013-10-23 22:12:13 -0700494 decoration_height = t->width + titlebar_height;
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500495 } else {
496 decoration_width = (t->width + t->margin) * 2;
497 decoration_height = t->width +
Kristian Høgsberg89f4bc42013-10-23 22:12:13 -0700498 titlebar_height + t->margin * 2;
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500499 }
500
501 frame_resize(frame, width + decoration_width,
502 height + decoration_height);
503}
504
505int32_t
506frame_width(struct frame *frame)
507{
508 return frame->width;
509}
510
511int32_t
512frame_height(struct frame *frame)
513{
514 return frame->height;
515}
516
517static void
518frame_refresh_geometry(struct frame *frame)
519{
520 struct frame_button *button;
521 struct theme *t = frame->theme;
Kristian Høgsberg89f4bc42013-10-23 22:12:13 -0700522 int x_l, x_r, y, w, h, titlebar_height;
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500523 int32_t decoration_width, decoration_height;
524
525 if (!frame->geometry_dirty)
526 return;
527
Boyan Dingc4902122014-07-04 15:19:22 +0800528 if (frame->title || !wl_list_empty(&frame->buttons))
Kristian Høgsberg89f4bc42013-10-23 22:12:13 -0700529 titlebar_height = t->titlebar_height;
530 else
531 titlebar_height = t->width;
532
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500533 if (frame->flags & FRAME_FLAG_MAXIMIZED) {
534 decoration_width = t->width * 2;
Kristian Høgsberg89f4bc42013-10-23 22:12:13 -0700535 decoration_height = t->width + titlebar_height;
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500536
537 frame->interior.x = t->width;
Kristian Høgsberg89f4bc42013-10-23 22:12:13 -0700538 frame->interior.y = titlebar_height;
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500539 frame->interior.width = frame->width - decoration_width;
540 frame->interior.height = frame->height - decoration_height;
541
542 frame->opaque_margin = 0;
543 frame->shadow_margin = 0;
544 } else {
545 decoration_width = (t->width + t->margin) * 2;
Kristian Høgsberg89f4bc42013-10-23 22:12:13 -0700546 decoration_height = t->width + titlebar_height + t->margin * 2;
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500547
548 frame->interior.x = t->width + t->margin;
Kristian Høgsberg89f4bc42013-10-23 22:12:13 -0700549 frame->interior.y = titlebar_height + t->margin;
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500550 frame->interior.width = frame->width - decoration_width;
551 frame->interior.height = frame->height - decoration_height;
552
553 frame->opaque_margin = t->margin + t->frame_radius;
554 frame->shadow_margin = t->margin;
555 }
556
557 x_r = frame->width - t->width - frame->shadow_margin;
558 x_l = t->width + frame->shadow_margin;
559 y = t->width + frame->shadow_margin;
560 wl_list_for_each(button, &frame->buttons, link) {
561 const int button_padding = 4;
562 w = cairo_image_surface_get_width(button->icon);
563 h = cairo_image_surface_get_height(button->icon);
564
565 if (button->flags & FRAME_BUTTON_DECORATED)
566 w += 10;
567
568 if (button->flags & FRAME_BUTTON_ALIGN_RIGHT) {
569 x_r -= w;
570
571 button->allocation.x = x_r;
572 button->allocation.y = y;
573 button->allocation.width = w + 1;
574 button->allocation.height = h + 1;
575
576 x_r -= button_padding;
577 } else {
578 button->allocation.x = x_l;
579 button->allocation.y = y;
580 button->allocation.width = w + 1;
581 button->allocation.height = h + 1;
582
583 x_l += w;
584 x_l += button_padding;
585 }
586 }
587
Louis-Francis Ratté-Boulianne864e39b2017-11-13 16:20:55 -0500588 frame->title_rect.x = x_l;
589 frame->title_rect.y = y;
590 frame->title_rect.width = x_r - x_l;
591 frame->title_rect.height = titlebar_height;
592
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500593 frame->geometry_dirty = 0;
594}
595
596void
597frame_interior(struct frame *frame, int32_t *x, int32_t *y,
598 int32_t *width, int32_t *height)
599{
600 frame_refresh_geometry(frame);
601
602 if (x)
603 *x = frame->interior.x;
604 if (y)
605 *y = frame->interior.y;
606 if (width)
607 *width = frame->interior.width;
608 if (height)
609 *height = frame->interior.height;
610}
611
612void
613frame_input_rect(struct frame *frame, int32_t *x, int32_t *y,
614 int32_t *width, int32_t *height)
615{
616 frame_refresh_geometry(frame);
617
618 if (x)
619 *x = frame->shadow_margin;
620 if (y)
621 *y = frame->shadow_margin;
622 if (width)
623 *width = frame->width - frame->shadow_margin * 2;
624 if (height)
625 *height = frame->height - frame->shadow_margin * 2;
626}
627
628void
629frame_opaque_rect(struct frame *frame, int32_t *x, int32_t *y,
630 int32_t *width, int32_t *height)
631{
632 frame_refresh_geometry(frame);
633
634 if (x)
635 *x = frame->opaque_margin;
636 if (y)
637 *y = frame->opaque_margin;
638 if (width)
639 *width = frame->width - frame->opaque_margin * 2;
640 if (height)
641 *height = frame->height - frame->opaque_margin * 2;
642}
643
Jasper St. Pierre74073452014-02-01 18:36:41 -0500644int
645frame_get_shadow_margin(struct frame *frame)
646{
647 frame_refresh_geometry(frame);
648
649 return frame->shadow_margin;
650}
651
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500652uint32_t
653frame_status(struct frame *frame)
654{
655 return frame->status;
656}
657
658void
659frame_status_clear(struct frame *frame, enum frame_status status)
660{
661 frame->status &= ~status;
662}
663
664static struct frame_button *
665frame_find_button(struct frame *frame, int x, int y)
666{
667 struct frame_button *button;
668 int rel_x, rel_y;
669
670 wl_list_for_each(button, &frame->buttons, link) {
671 rel_x = x - button->allocation.x;
672 rel_y = y - button->allocation.y;
673
674 if (0 <= rel_x && rel_x < button->allocation.width &&
675 0 <= rel_y && rel_y < button->allocation.height)
676 return button;
677 }
678
679 return NULL;
680}
681
682enum theme_location
683frame_pointer_enter(struct frame *frame, void *data, int x, int y)
684{
685 return frame_pointer_motion(frame, data, x, y);
686}
687
688enum theme_location
689frame_pointer_motion(struct frame *frame, void *data, int x, int y)
690{
691 struct frame_pointer *pointer = frame_pointer_get(frame, data);
692 struct frame_button *button = frame_find_button(frame, x, y);
693 enum theme_location location;
694
695 location = theme_get_location(frame->theme, x, y,
696 frame->width, frame->height,
697 frame->flags & FRAME_FLAG_MAXIMIZED ?
698 THEME_FRAME_MAXIMIZED : 0);
699 if (!pointer)
700 return location;
701
702 pointer->x = x;
703 pointer->y = y;
704
705 if (pointer->hover_button == button)
706 return location;
707
708 if (pointer->hover_button)
709 frame_button_leave(pointer->hover_button, pointer);
710
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500711 pointer->hover_button = button;
712
713 if (pointer->hover_button)
714 frame_button_enter(pointer->hover_button);
715
716 return location;
717}
718
Jason Ekstrand0bdd58f2013-10-27 22:25:03 -0500719static void
720frame_pointer_button_destroy(struct frame_pointer_button *button)
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500721{
Jason Ekstrand0bdd58f2013-10-27 22:25:03 -0500722 wl_list_remove(&button->link);
723 free(button);
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500724}
725
Jason Ekstrand0bdd58f2013-10-27 22:25:03 -0500726static void
727frame_pointer_button_press(struct frame *frame, struct frame_pointer *pointer,
728 struct frame_pointer_button *button)
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500729{
Jason Ekstrand0bdd58f2013-10-27 22:25:03 -0500730 if (button->button == BTN_RIGHT) {
731 if (button->press_location == THEME_LOCATION_TITLEBAR)
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500732 frame->status |= FRAME_STATUS_MENU;
733
Jason Ekstrand0bdd58f2013-10-27 22:25:03 -0500734 frame_pointer_button_destroy(button);
735
736 } else if (button->button == BTN_LEFT) {
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500737 if (pointer->hover_button) {
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500738 frame_button_press(pointer->hover_button);
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500739 } else {
Jason Ekstrand0bdd58f2013-10-27 22:25:03 -0500740 switch (button->press_location) {
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500741 case THEME_LOCATION_TITLEBAR:
742 frame->status |= FRAME_STATUS_MOVE;
Jason Ekstrand0bdd58f2013-10-27 22:25:03 -0500743
744 frame_pointer_button_destroy(button);
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500745 break;
746 case THEME_LOCATION_RESIZING_TOP:
747 case THEME_LOCATION_RESIZING_BOTTOM:
748 case THEME_LOCATION_RESIZING_LEFT:
749 case THEME_LOCATION_RESIZING_RIGHT:
750 case THEME_LOCATION_RESIZING_TOP_LEFT:
751 case THEME_LOCATION_RESIZING_TOP_RIGHT:
752 case THEME_LOCATION_RESIZING_BOTTOM_LEFT:
753 case THEME_LOCATION_RESIZING_BOTTOM_RIGHT:
754 frame->status |= FRAME_STATUS_RESIZE;
Jason Ekstrand0bdd58f2013-10-27 22:25:03 -0500755
756 frame_pointer_button_destroy(button);
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500757 break;
758 default:
759 break;
760 }
761 }
Jason Ekstrand0bdd58f2013-10-27 22:25:03 -0500762 }
763}
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500764
Jason Ekstrand0bdd58f2013-10-27 22:25:03 -0500765static void
766frame_pointer_button_release(struct frame *frame, struct frame_pointer *pointer,
767 struct frame_pointer_button *button)
768{
769 if (button->button == BTN_LEFT && button->frame_button) {
770 if (button->frame_button == pointer->hover_button)
771 frame_button_release(button->frame_button);
772 else
773 frame_button_cancel(button->frame_button);
774 }
775}
776
777static void
778frame_pointer_button_cancel(struct frame *frame, struct frame_pointer *pointer,
779 struct frame_pointer_button *button)
780{
781 if (button->frame_button)
782 frame_button_cancel(button->frame_button);
783}
784
785void
786frame_pointer_leave(struct frame *frame, void *data)
787{
788 struct frame_pointer *pointer = frame_pointer_get(frame, data);
789 struct frame_pointer_button *button, *next;
790 if (!pointer)
791 return;
792
793 if (pointer->hover_button)
794 frame_button_leave(pointer->hover_button, pointer);
795
796 wl_list_for_each_safe(button, next, &pointer->down_buttons, link) {
797 frame_pointer_button_cancel(frame, pointer, button);
798 frame_pointer_button_destroy(button);
799 }
800
801 frame_pointer_destroy(pointer);
802}
803
804enum theme_location
805frame_pointer_button(struct frame *frame, void *data,
Quentin Glidicd8b17bc2016-07-10 11:00:55 +0200806 uint32_t btn, enum wl_pointer_button_state state)
Jason Ekstrand0bdd58f2013-10-27 22:25:03 -0500807{
808 struct frame_pointer *pointer = frame_pointer_get(frame, data);
809 struct frame_pointer_button *button;
U. Artie Eoff6d71c3c2014-01-17 14:44:05 -0800810 enum theme_location location = THEME_LOCATION_EXTERIOR;
811
812 if (!pointer)
813 return location;
Jason Ekstrand0bdd58f2013-10-27 22:25:03 -0500814
815 location = theme_get_location(frame->theme, pointer->x, pointer->y,
816 frame->width, frame->height,
817 frame->flags & FRAME_FLAG_MAXIMIZED ?
818 THEME_FRAME_MAXIMIZED : 0);
819
Quentin Glidicd8b17bc2016-07-10 11:00:55 +0200820 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
Jason Ekstrand0bdd58f2013-10-27 22:25:03 -0500821 button = malloc(sizeof *button);
822 if (!button)
823 return location;
824
825 button->button = btn;
826 button->press_location = location;
827 button->frame_button = pointer->hover_button;
828 wl_list_insert(&pointer->down_buttons, &button->link);
829
830 frame_pointer_button_press(frame, pointer, button);
Quentin Glidicd8b17bc2016-07-10 11:00:55 +0200831 } else if (state == WL_POINTER_BUTTON_STATE_RELEASED) {
Jason Ekstrand0bdd58f2013-10-27 22:25:03 -0500832 button = NULL;
833 wl_list_for_each(button, &pointer->down_buttons, link)
834 if (button->button == btn)
835 break;
836 /* Make sure we didn't hit the end */
837 if (&button->link == &pointer->down_buttons)
838 return location;
839
840 location = button->press_location;
841 frame_pointer_button_release(frame, pointer, button);
842 frame_pointer_button_destroy(button);
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500843 }
844
845 return location;
846}
847
Derek Foreman96906412015-11-06 15:56:07 -0600848enum theme_location
Jason Ekstrand3f66cf92013-10-13 19:08:40 -0500849frame_touch_down(struct frame *frame, void *data, int32_t id, int x, int y)
850{
851 struct frame_touch *touch = frame_touch_get(frame, data);
852 struct frame_button *button = frame_find_button(frame, x, y);
853 enum theme_location location;
854
Jason Ekstrand3f66cf92013-10-13 19:08:40 -0500855 location = theme_get_location(frame->theme, x, y,
856 frame->width, frame->height,
857 frame->flags & FRAME_FLAG_MAXIMIZED ?
858 THEME_FRAME_MAXIMIZED : 0);
859
Derek Foreman96906412015-11-06 15:56:07 -0600860 if (id > 0)
861 return location;
862
863 if (touch && button) {
864 touch->button = button;
865 frame_button_press(touch->button);
866 return location;
867 }
868
Jason Ekstrand3f66cf92013-10-13 19:08:40 -0500869 switch (location) {
870 case THEME_LOCATION_TITLEBAR:
871 frame->status |= FRAME_STATUS_MOVE;
872 break;
873 case THEME_LOCATION_RESIZING_TOP:
874 case THEME_LOCATION_RESIZING_BOTTOM:
875 case THEME_LOCATION_RESIZING_LEFT:
876 case THEME_LOCATION_RESIZING_RIGHT:
877 case THEME_LOCATION_RESIZING_TOP_LEFT:
878 case THEME_LOCATION_RESIZING_TOP_RIGHT:
879 case THEME_LOCATION_RESIZING_BOTTOM_LEFT:
880 case THEME_LOCATION_RESIZING_BOTTOM_RIGHT:
881 frame->status |= FRAME_STATUS_RESIZE;
882 break;
883 default:
884 break;
885 }
Derek Foreman96906412015-11-06 15:56:07 -0600886 return location;
Jason Ekstrand3f66cf92013-10-13 19:08:40 -0500887}
888
889void
890frame_touch_up(struct frame *frame, void *data, int32_t id)
891{
892 struct frame_touch *touch = frame_touch_get(frame, data);
893
894 if (id > 0)
895 return;
896
U. Artie Eoff6d71c3c2014-01-17 14:44:05 -0800897 if (touch && touch->button) {
Jason Ekstrand3f66cf92013-10-13 19:08:40 -0500898 frame_button_release(touch->button);
899 frame_touch_destroy(touch);
Jason Ekstrand3f66cf92013-10-13 19:08:40 -0500900 }
901}
902
Xiong Zhangbfb4ade2014-06-12 11:06:25 +0800903enum theme_location
904frame_double_click(struct frame *frame, void *data,
Quentin Glidicd8b17bc2016-07-10 11:00:55 +0200905 uint32_t btn, enum wl_pointer_button_state state)
Xiong Zhangbfb4ade2014-06-12 11:06:25 +0800906{
907 struct frame_pointer *pointer = frame_pointer_get(frame, data);
908 struct frame_button *button;
909 enum theme_location location = THEME_LOCATION_EXTERIOR;
910
911 location = theme_get_location(frame->theme, pointer->x, pointer->y,
912 frame->width, frame->height,
913 frame->flags & FRAME_FLAG_MAXIMIZED ?
914 THEME_FRAME_MAXIMIZED : 0);
915
916 button = frame_find_button(frame, pointer->x, pointer->y);
917
918 if (location != THEME_LOCATION_TITLEBAR || btn != BTN_LEFT)
919 return location;
920
Quentin Glidicd8b17bc2016-07-10 11:00:55 +0200921 if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
Xiong Zhangbfb4ade2014-06-12 11:06:25 +0800922 if (button)
923 frame_button_press(button);
924 else
925 frame->status |= FRAME_STATUS_MAXIMIZE;
Quentin Glidicd8b17bc2016-07-10 11:00:55 +0200926 } else if (state == WL_POINTER_BUTTON_STATE_RELEASED) {
Xiong Zhangbfb4ade2014-06-12 11:06:25 +0800927 if (button)
928 frame_button_release(button);
929 }
930
931 return location;
932}
933
Jason Ekstrand3f66cf92013-10-13 19:08:40 -0500934void
Xiong Zhang382de462014-06-12 11:06:26 +0800935frame_double_touch_down(struct frame *frame, void *data, int32_t id,
936 int x, int y)
937{
938 struct frame_touch *touch = frame_touch_get(frame, data);
939 struct frame_button *button = frame_find_button(frame, x, y);
940 enum theme_location location;
941
942 if (touch && button) {
943 touch->button = button;
944 frame_button_press(touch->button);
945 return;
946 }
947
948 location = theme_get_location(frame->theme, x, y,
949 frame->width, frame->height,
950 frame->flags & FRAME_FLAG_MAXIMIZED ?
951 THEME_FRAME_MAXIMIZED : 0);
952
953 switch (location) {
954 case THEME_LOCATION_TITLEBAR:
955 frame->status |= FRAME_STATUS_MAXIMIZE;
956 break;
957 case THEME_LOCATION_RESIZING_TOP:
958 case THEME_LOCATION_RESIZING_BOTTOM:
959 case THEME_LOCATION_RESIZING_LEFT:
960 case THEME_LOCATION_RESIZING_RIGHT:
961 case THEME_LOCATION_RESIZING_TOP_LEFT:
962 case THEME_LOCATION_RESIZING_TOP_RIGHT:
963 case THEME_LOCATION_RESIZING_BOTTOM_LEFT:
964 case THEME_LOCATION_RESIZING_BOTTOM_RIGHT:
965 frame->status |= FRAME_STATUS_RESIZE;
966 break;
967 default:
968 break;
969 }
970}
971
972void
973frame_double_touch_up(struct frame *frame, void *data, int32_t id)
974{
975 struct frame_touch *touch = frame_touch_get(frame, data);
976
977 if (touch && touch->button) {
978 frame_button_release(touch->button);
979 frame_touch_destroy(touch);
980 }
981}
982
983void
Jason Ekstrand01c9ec32013-10-13 19:08:39 -0500984frame_repaint(struct frame *frame, cairo_t *cr)
985{
986 struct frame_button *button;
987 uint32_t flags = 0;
988
989 frame_refresh_geometry(frame);
990
991 if (frame->flags & FRAME_FLAG_MAXIMIZED)
992 flags |= THEME_FRAME_MAXIMIZED;
993
994 if (frame->flags & FRAME_FLAG_ACTIVE)
995 flags |= THEME_FRAME_ACTIVE;
996
997 cairo_save(cr);
998 theme_render_frame(frame->theme, cr, frame->width, frame->height,
Louis-Francis Ratté-Boulianne864e39b2017-11-13 16:20:55 -0500999 frame->title, &frame->title_rect,
1000 &frame->buttons, flags);
Jason Ekstrand01c9ec32013-10-13 19:08:39 -05001001 cairo_restore(cr);
1002
1003 wl_list_for_each(button, &frame->buttons, link)
1004 frame_button_repaint(button, cr);
1005
1006 frame_status_clear(frame, FRAME_STATUS_REPAINT);
1007}