1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 
3 /* Metacity window frame manager widget */
4 
5 /*
6  * Copyright (C) 2001 Havoc Pennington
7  * Copyright (C) 2003 Red Hat, Inc.
8  * Copyright (C) 2005, 2006 Elijah Newren
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License as
12  * published by the Free Software Foundation; either version 2 of the
13  * License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, see <http://www.gnu.org/licenses/>.
22  */
23 
24 #include "config.h"
25 
26 #include <cairo-xlib.h>
27 #include <math.h>
28 #include <string.h>
29 
30 #include "core/frame.h"
31 #include "core/window-private.h"
32 #include "meta/boxes.h"
33 #include "meta/prefs.h"
34 #include "meta/theme.h"
35 #include "meta/util.h"
36 #include "ui/ui.h"
37 #include "ui/frames.h"
38 #include "x11/meta-x11-window-control.h"
39 #include "x11/window-x11-private.h"
40 #include "x11/window-x11.h"
41 
42 #define DEFAULT_INNER_BUTTON_BORDER 3
43 
44 static void meta_frames_destroy       (GtkWidget       *object);
45 static void meta_frames_finalize      (GObject         *object);
46 static void meta_frames_style_updated (GtkWidget       *widget);
47 
48 static gboolean meta_frames_draw                  (GtkWidget           *widget,
49                                                    cairo_t             *cr);
50 
51 static void meta_ui_frame_attach_style (MetaUIFrame *frame);
52 
53 static void meta_ui_frame_paint        (MetaUIFrame  *frame,
54                                         cairo_t      *cr);
55 
56 static void meta_ui_frame_calc_geometry (MetaUIFrame       *frame,
57                                          MetaFrameGeometry *fgeom);
58 
59 static void meta_ui_frame_update_prelit_control (MetaUIFrame     *frame,
60                                                  MetaFrameControl control);
61 
62 static void meta_frames_font_changed          (MetaFrames *frames);
63 static void meta_frames_button_layout_changed (MetaFrames *frames);
64 
65 
66 static GdkRectangle*    control_rect (MetaFrameControl   control,
67                                       MetaFrameGeometry *fgeom);
68 static MetaFrameControl get_control  (MetaUIFrame       *frame,
69                                       int                x,
70                                       int                y);
71 
72 G_DEFINE_TYPE (MetaFrames, meta_frames, GTK_TYPE_WINDOW);
73 
74 enum
75 {
76   META_ACTION_CLICK,
77   META_ACTION_RIGHT_CLICK,
78   META_ACTION_MIDDLE_CLICK,
79   META_ACTION_DOUBLE_CLICK,
80   META_ACTION_IGNORE
81 };
82 
83 static GObject *
meta_frames_constructor(GType gtype,guint n_properties,GObjectConstructParam * properties)84 meta_frames_constructor (GType                  gtype,
85                          guint                  n_properties,
86                          GObjectConstructParam *properties)
87 {
88   GObject *object;
89   GObjectClass *gobject_class;
90 
91   gobject_class = G_OBJECT_CLASS (meta_frames_parent_class);
92   object = gobject_class->constructor (gtype, n_properties, properties);
93 
94   g_object_set (object,
95                 "type", GTK_WINDOW_POPUP,
96                 NULL);
97 
98   return object;
99 }
100 
101 static void
meta_frames_class_init(MetaFramesClass * class)102 meta_frames_class_init (MetaFramesClass *class)
103 {
104   GObjectClass   *gobject_class;
105   GtkWidgetClass *widget_class;
106 
107   gobject_class = G_OBJECT_CLASS (class);
108   widget_class = (GtkWidgetClass*) class;
109 
110   gobject_class->constructor = meta_frames_constructor;
111   gobject_class->finalize = meta_frames_finalize;
112 
113   widget_class->destroy = meta_frames_destroy;
114 
115   widget_class->style_updated = meta_frames_style_updated;
116 
117   widget_class->draw = meta_frames_draw;
118 }
119 
120 static gint
unsigned_long_equal(gconstpointer v1,gconstpointer v2)121 unsigned_long_equal (gconstpointer v1,
122                      gconstpointer v2)
123 {
124   return *((const gulong*) v1) == *((const gulong*) v2);
125 }
126 
127 static guint
unsigned_long_hash(gconstpointer v)128 unsigned_long_hash (gconstpointer v)
129 {
130   gulong val = * (const gulong *) v;
131 
132   /* I'm not sure this works so well. */
133 #if GLIB_SIZEOF_LONG > 4
134   return (guint) (val ^ (val >> 32));
135 #else
136   return val;
137 #endif
138 }
139 
140 static void
prefs_changed_callback(MetaPreference pref,void * data)141 prefs_changed_callback (MetaPreference pref,
142                         void          *data)
143 {
144   switch (pref)
145     {
146     case META_PREF_TITLEBAR_FONT:
147       meta_frames_font_changed (META_FRAMES (data));
148       break;
149     case META_PREF_BUTTON_LAYOUT:
150       meta_frames_button_layout_changed (META_FRAMES (data));
151       break;
152     default:
153       break;
154     }
155 }
156 
157 static void
invalidate_whole_window(MetaUIFrame * frame)158 invalidate_whole_window (MetaUIFrame *frame)
159 {
160   if (!frame->is_frozen)
161     {
162       meta_window_x11_freeze_commits (frame->meta_window);
163       frame->is_frozen = TRUE;
164     }
165   gdk_window_invalidate_rect (frame->window, NULL, FALSE);
166 }
167 
168 static MetaStyleInfo *
meta_frames_get_theme_variant(MetaFrames * frames,const gchar * variant)169 meta_frames_get_theme_variant (MetaFrames  *frames,
170                                const gchar *variant)
171 {
172   MetaStyleInfo *style_info;
173 
174   style_info = g_hash_table_lookup (frames->style_variants, variant);
175   if (style_info == NULL)
176     {
177       style_info = meta_theme_create_style_info (gtk_widget_get_screen (GTK_WIDGET (frames)), variant);
178       g_hash_table_insert (frames->style_variants, g_strdup (variant), style_info);
179     }
180 
181   return style_info;
182 }
183 
184 static void
update_style_contexts(MetaFrames * frames)185 update_style_contexts (MetaFrames *frames)
186 {
187   MetaStyleInfo *style_info;
188   GList *variants, *variant;
189   GdkScreen *screen;
190 
191   screen = gtk_widget_get_screen (GTK_WIDGET (frames));
192 
193   if (frames->normal_style)
194     meta_style_info_unref (frames->normal_style);
195   frames->normal_style = meta_theme_create_style_info (screen, NULL);
196 
197   variants = g_hash_table_get_keys (frames->style_variants);
198   for (variant = variants; variant; variant = variant->next)
199     {
200       style_info = meta_theme_create_style_info (screen, (char *)variant->data);
201       g_hash_table_insert (frames->style_variants,
202                            g_strdup (variant->data), style_info);
203     }
204   g_list_free (variants);
205 }
206 
207 static void
meta_frames_init(MetaFrames * frames)208 meta_frames_init (MetaFrames *frames)
209 {
210   frames->text_heights = g_hash_table_new (NULL, NULL);
211 
212   frames->frames = g_hash_table_new (unsigned_long_hash, unsigned_long_equal);
213 
214   frames->style_variants = g_hash_table_new_full (g_str_hash, g_str_equal,
215                                                   g_free, (GDestroyNotify)meta_style_info_unref);
216 
217   update_style_contexts (frames);
218 
219   meta_prefs_add_listener (prefs_changed_callback, frames);
220 }
221 
222 static void
listify_func(gpointer key,gpointer value,gpointer data)223 listify_func (gpointer key, gpointer value, gpointer data)
224 {
225   GSList **listp;
226 
227   listp = data;
228   *listp = g_slist_prepend (*listp, value);
229 }
230 
231 static void
meta_frames_destroy(GtkWidget * object)232 meta_frames_destroy (GtkWidget *object)
233 {
234   GSList *winlist;
235   GSList *tmp;
236   MetaFrames *frames;
237 
238   frames = META_FRAMES (object);
239 
240   winlist = NULL;
241   g_hash_table_foreach (frames->frames, listify_func, &winlist);
242 
243   /* Unmanage all frames */
244   for (tmp = winlist; tmp != NULL; tmp = tmp->next)
245     {
246       MetaUIFrame *frame = tmp->data;
247       meta_ui_frame_unmanage (frame);
248     }
249   g_slist_free (winlist);
250 
251   if (frames->normal_style)
252     {
253       meta_style_info_unref (frames->normal_style);
254       frames->normal_style = NULL;
255     }
256 
257   if (frames->style_variants)
258     {
259       g_hash_table_destroy (frames->style_variants);
260       frames->style_variants = NULL;
261     }
262 
263   GTK_WIDGET_CLASS (meta_frames_parent_class)->destroy (object);
264 }
265 
266 static void
meta_frames_finalize(GObject * object)267 meta_frames_finalize (GObject *object)
268 {
269   MetaFrames *frames;
270 
271   frames = META_FRAMES (object);
272 
273   meta_prefs_remove_listener (prefs_changed_callback, frames);
274 
275   g_hash_table_destroy (frames->text_heights);
276 
277   g_assert (g_hash_table_size (frames->frames) == 0);
278   g_hash_table_destroy (frames->frames);
279 
280   G_OBJECT_CLASS (meta_frames_parent_class)->finalize (object);
281 }
282 
283 static void
queue_recalc_func(gpointer key,gpointer value,gpointer user_data)284 queue_recalc_func (gpointer key,
285                    gpointer value,
286                    gpointer user_data)
287 {
288   MetaUIFrame *frame = value;
289   MetaFrames *frames = user_data;
290 
291   invalidate_whole_window (frame);
292   meta_x11_wm_queue_frame_resize (frames->x11_display,
293                                   frame->xwindow);
294 
295   g_clear_object (&frame->text_layout);
296 }
297 
298 static void
meta_frames_font_changed(MetaFrames * frames)299 meta_frames_font_changed (MetaFrames *frames)
300 {
301   if (g_hash_table_size (frames->text_heights) > 0)
302     {
303       g_hash_table_destroy (frames->text_heights);
304       frames->text_heights = g_hash_table_new (NULL, NULL);
305     }
306 
307   /* Queue a draw/resize on all frames */
308   g_hash_table_foreach (frames->frames,
309                         queue_recalc_func, frames);
310 
311 }
312 
313 static void
queue_draw_func(gpointer key,gpointer value,gpointer data)314 queue_draw_func (gpointer key, gpointer value, gpointer data)
315 {
316   MetaUIFrame *frame = value;
317   invalidate_whole_window (frame);
318 }
319 
320 static void
meta_frames_button_layout_changed(MetaFrames * frames)321 meta_frames_button_layout_changed (MetaFrames *frames)
322 {
323   g_hash_table_foreach (frames->frames,
324                         queue_draw_func, frames);
325 }
326 
327 static void
reattach_style_func(gpointer key,gpointer value,gpointer data)328 reattach_style_func (gpointer key, gpointer value, gpointer data)
329 {
330   MetaUIFrame *frame = value;
331   meta_ui_frame_attach_style (frame);
332 }
333 
334 static void
meta_frames_style_updated(GtkWidget * widget)335 meta_frames_style_updated  (GtkWidget *widget)
336 {
337   MetaFrames *frames;
338 
339   frames = META_FRAMES (widget);
340 
341   meta_frames_font_changed (frames);
342 
343   update_style_contexts (frames);
344 
345   g_hash_table_foreach (frames->frames, reattach_style_func, NULL);
346 
347   meta_display_queue_retheme_all_windows (meta_get_display ());
348 
349   GTK_WIDGET_CLASS (meta_frames_parent_class)->style_updated (widget);
350 }
351 
352 static void
meta_ui_frame_ensure_layout(MetaUIFrame * frame,MetaFrameType type)353 meta_ui_frame_ensure_layout (MetaUIFrame    *frame,
354                              MetaFrameType   type)
355 {
356   MetaFrames *frames = frame->frames;
357   GtkWidget *widget;
358   MetaFrameLayout *layout;
359 
360   widget = GTK_WIDGET (frames);
361 
362   g_return_if_fail (gtk_widget_get_realized (widget));
363 
364   layout = meta_theme_get_frame_layout (meta_theme_get_default (), type);
365 
366   if (layout != frame->cache_layout)
367     g_clear_object (&frame->text_layout);
368 
369   frame->cache_layout = layout;
370 
371   if (frame->text_layout == NULL)
372     {
373       gpointer key, value;
374       PangoFontDescription *font_desc;
375       int size;
376 
377       frame->text_layout = gtk_widget_create_pango_layout (widget, frame->title);
378 
379       pango_layout_set_ellipsize (frame->text_layout, PANGO_ELLIPSIZE_END);
380       pango_layout_set_auto_dir (frame->text_layout, FALSE);
381       pango_layout_set_single_paragraph_mode (frame->text_layout, TRUE);
382 
383       font_desc = meta_style_info_create_font_desc (frame->style_info);
384       meta_frame_layout_apply_scale (layout, font_desc);
385 
386       size = pango_font_description_get_size (font_desc);
387 
388       if (g_hash_table_lookup_extended (frames->text_heights,
389                                         GINT_TO_POINTER (size),
390                                         &key, &value))
391         {
392           frame->text_height = GPOINTER_TO_INT (value);
393         }
394       else
395         {
396           frame->text_height =
397             meta_pango_font_desc_get_text_height (font_desc,
398                                                   gtk_widget_get_pango_context (widget));
399 
400           g_hash_table_replace (frames->text_heights,
401                                 GINT_TO_POINTER (size),
402                                 GINT_TO_POINTER (frame->text_height));
403         }
404 
405       pango_layout_set_font_description (frame->text_layout,
406                                          font_desc);
407 
408       pango_font_description_free (font_desc);
409     }
410 }
411 
412 static void
meta_ui_frame_calc_geometry(MetaUIFrame * frame,MetaFrameGeometry * fgeom)413 meta_ui_frame_calc_geometry (MetaUIFrame       *frame,
414                              MetaFrameGeometry *fgeom)
415 {
416   MetaFrameFlags flags;
417   MetaFrameType type;
418   MetaButtonLayout button_layout;
419   MetaWindowX11 *window_x11 = META_WINDOW_X11 (frame->meta_window);
420   MetaRectangle client_rect;
421 
422   flags = meta_frame_get_flags (frame->meta_window->frame);
423   type = meta_window_get_frame_type (frame->meta_window);
424 
425   meta_ui_frame_ensure_layout (frame, type);
426 
427   meta_prefs_get_button_layout (&button_layout);
428 
429   client_rect = meta_window_x11_get_client_rect (window_x11);
430 
431   meta_theme_calc_geometry (meta_theme_get_default (),
432                             frame->style_info,
433                             type,
434                             frame->text_height,
435                             flags,
436                             client_rect.width,
437                             client_rect.height,
438                             &button_layout,
439                             fgeom);
440 }
441 
442 MetaFrames*
meta_frames_new(MetaX11Display * x11_display)443 meta_frames_new (MetaX11Display *x11_display)
444 {
445   MetaFrames *frames;
446 
447   frames = g_object_new (META_TYPE_FRAMES,
448                          "type", GTK_WINDOW_POPUP,
449                          NULL);
450   frames->x11_display = x11_display;
451 
452   /* Put the window at an arbitrary offscreen location; the one place
453    * it can't be is at -100x-100, since the meta_window_new() will
454    * mistake it for a window created via meta_create_offscreen_window()
455    * and ignore it, and we need this window to get frame-synchronization
456    * messages so that GTK+'s style change handling works.
457    */
458   gtk_window_move (GTK_WINDOW (frames), -200, -200);
459   gtk_window_resize (GTK_WINDOW (frames), 1, 1);
460 
461   return frames;
462 }
463 
464 static const char *
get_global_theme_variant(MetaFrames * frames)465 get_global_theme_variant (MetaFrames *frames)
466 {
467   GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (frames));
468   GtkSettings *settings = gtk_settings_get_for_screen (screen);
469   gboolean dark_theme_requested;
470 
471   g_object_get (settings,
472                 "gtk-application-prefer-dark-theme", &dark_theme_requested,
473                 NULL);
474 
475   if (dark_theme_requested)
476     return "dark";
477 
478   return NULL;
479 }
480 
481 /* In order to use a style with a window it has to be attached to that
482  * window. Actually, the colormaps just have to match, but since GTK+
483  * already takes care of making sure that its cheap to attach a style
484  * to multiple windows with the same colormap, we can just go ahead
485  * and attach separately for each window.
486  */
487 static void
meta_ui_frame_attach_style(MetaUIFrame * frame)488 meta_ui_frame_attach_style (MetaUIFrame *frame)
489 {
490   MetaFrames *frames = frame->frames;
491   const char *variant;
492 
493   if (frame->style_info != NULL)
494     meta_style_info_unref (frame->style_info);
495 
496   variant = frame->meta_window->gtk_theme_variant;
497   if (variant == NULL)
498     variant = get_global_theme_variant (frame->frames);
499 
500   if (variant == NULL || *variant == '\0')
501     frame->style_info = meta_style_info_ref (frames->normal_style);
502   else
503     frame->style_info = meta_style_info_ref (meta_frames_get_theme_variant (frames,
504                                                                             variant));
505 }
506 
507 MetaUIFrame *
meta_frames_manage_window(MetaFrames * frames,MetaWindow * meta_window,Window xwindow,GdkWindow * window)508 meta_frames_manage_window (MetaFrames *frames,
509                            MetaWindow *meta_window,
510                            Window      xwindow,
511                            GdkWindow  *window)
512 {
513   MetaUIFrame *frame;
514 
515   g_assert (window);
516 
517   frame = g_new (MetaUIFrame, 1);
518 
519   frame->frames = frames;
520   frame->window = window;
521 
522   gdk_window_set_user_data (frame->window, frames);
523 
524   frame->style_info = NULL;
525 
526   /* Don't set event mask here, it's in frame.c */
527 
528   frame->xwindow = xwindow;
529   frame->meta_window = meta_window;
530   frame->cache_layout = NULL;
531   frame->text_layout = NULL;
532   frame->text_height = -1;
533   frame->title = NULL;
534   frame->prelit_control = META_FRAME_CONTROL_NONE;
535   frame->button_state = META_BUTTON_STATE_NORMAL;
536   frame->is_frozen = FALSE;
537 
538   meta_x11_wm_grab_buttons (frames->x11_display, frame->xwindow);
539 
540   g_hash_table_replace (frames->frames, &frame->xwindow, frame);
541 
542   return frame;
543 }
544 
545 void
meta_ui_frame_unmanage(MetaUIFrame * frame)546 meta_ui_frame_unmanage (MetaUIFrame *frame)
547 {
548   MetaFrames *frames = frame->frames;
549 
550   /* restore the cursor */
551   meta_x11_wm_set_screen_cursor (frames->x11_display,
552                                  frame->xwindow,
553                                  META_CURSOR_DEFAULT);
554 
555   gdk_window_set_user_data (frame->window, NULL);
556 
557   g_hash_table_remove (frames->frames, &frame->xwindow);
558 
559   meta_style_info_unref (frame->style_info);
560 
561   gdk_window_destroy (frame->window);
562 
563   if (frame->text_layout)
564     g_object_unref (G_OBJECT (frame->text_layout));
565 
566   if (frame->is_frozen)
567     meta_window_x11_thaw_commits (frame->meta_window);
568 
569   g_free (frame->title);
570 
571   g_free (frame);
572 }
573 
574 void
meta_ui_frame_get_borders(MetaUIFrame * frame,MetaFrameBorders * borders)575 meta_ui_frame_get_borders (MetaUIFrame *frame,
576                            MetaFrameBorders *borders)
577 {
578   MetaFrameFlags flags;
579   MetaFrameType type;
580 
581   flags = meta_frame_get_flags (frame->meta_window->frame);
582   type = meta_window_get_frame_type (frame->meta_window);
583 
584   g_return_if_fail (type < META_FRAME_TYPE_LAST);
585 
586   meta_ui_frame_ensure_layout (frame, type);
587 
588   /* We can't get the full geometry, because that depends on
589    * the client window size and probably we're being called
590    * by the core move/resize code to decide on the client
591    * window size
592    */
593   meta_theme_get_frame_borders (meta_theme_get_default (),
594                                 frame->style_info,
595                                 type,
596                                 frame->text_height,
597                                 flags,
598                                 borders);
599 }
600 
601 /* The visible frame rectangle surrounds the visible portion of the
602  * frame window; it subtracts only the invisible borders from the frame
603  * window's size.
604  */
605 static void
get_visible_frame_rect(MetaFrameGeometry * fgeom,cairo_rectangle_int_t * rect)606 get_visible_frame_rect (MetaFrameGeometry     *fgeom,
607                         cairo_rectangle_int_t *rect)
608 {
609   rect->x = fgeom->borders.invisible.left;
610   rect->y = fgeom->borders.invisible.top;
611   rect->width = fgeom->width - fgeom->borders.invisible.right - rect->x;
612   rect->height = fgeom->height - fgeom->borders.invisible.bottom - rect->y;
613 }
614 
615 static cairo_region_t *
get_visible_region(MetaUIFrame * frame,MetaFrameGeometry * fgeom)616 get_visible_region (MetaUIFrame       *frame,
617                     MetaFrameGeometry *fgeom)
618 {
619   cairo_region_t *corners_region;
620   cairo_region_t *visible_region;
621   cairo_rectangle_int_t rect;
622   cairo_rectangle_int_t frame_rect;
623 
624   corners_region = cairo_region_create ();
625   get_visible_frame_rect (fgeom, &frame_rect);
626 
627   if (fgeom->top_left_corner_rounded_radius != 0)
628     {
629       const int corner = fgeom->top_left_corner_rounded_radius;
630       const float radius = corner;
631       int i;
632 
633       for (i=0; i<corner; i++)
634         {
635           const int width = floor(0.5 + radius - sqrt(radius*radius - (radius-(i+0.5))*(radius-(i+0.5))));
636           rect.x = frame_rect.x;
637           rect.y = frame_rect.y + i;
638           rect.width = width;
639           rect.height = 1;
640 
641           cairo_region_union_rectangle (corners_region, &rect);
642         }
643     }
644 
645   if (fgeom->top_right_corner_rounded_radius != 0)
646     {
647       const int corner = fgeom->top_right_corner_rounded_radius;
648       const float radius = corner;
649       int i;
650 
651       for (i=0; i<corner; i++)
652         {
653           const int width = floor(0.5 + radius - sqrt(radius*radius - (radius-(i+0.5))*(radius-(i+0.5))));
654           rect.x = frame_rect.x + frame_rect.width - width;
655           rect.y = frame_rect.y + i;
656           rect.width = width;
657           rect.height = 1;
658 
659           cairo_region_union_rectangle (corners_region, &rect);
660         }
661     }
662 
663   if (fgeom->bottom_left_corner_rounded_radius != 0)
664     {
665       const int corner = fgeom->bottom_left_corner_rounded_radius;
666       const float radius = corner;
667       int i;
668 
669       for (i=0; i<corner; i++)
670         {
671           const int width = floor(0.5 + radius - sqrt(radius*radius - (radius-(i+0.5))*(radius-(i+0.5))));
672           rect.x = frame_rect.x;
673           rect.y = frame_rect.y + frame_rect.height - i - 1;
674           rect.width = width;
675           rect.height = 1;
676 
677           cairo_region_union_rectangle (corners_region, &rect);
678         }
679     }
680 
681   if (fgeom->bottom_right_corner_rounded_radius != 0)
682     {
683       const int corner = fgeom->bottom_right_corner_rounded_radius;
684       const float radius = corner;
685       int i;
686 
687       for (i=0; i<corner; i++)
688         {
689           const int width = floor(0.5 + radius - sqrt(radius*radius - (radius-(i+0.5))*(radius-(i+0.5))));
690           rect.x = frame_rect.x + frame_rect.width - width;
691           rect.y = frame_rect.y + frame_rect.height - i - 1;
692           rect.width = width;
693           rect.height = 1;
694 
695           cairo_region_union_rectangle (corners_region, &rect);
696         }
697     }
698 
699   visible_region = cairo_region_create_rectangle (&frame_rect);
700   cairo_region_subtract (visible_region, corners_region);
701   cairo_region_destroy (corners_region);
702 
703   return visible_region;
704 }
705 
706 cairo_region_t *
meta_ui_frame_get_bounds(MetaUIFrame * frame)707 meta_ui_frame_get_bounds (MetaUIFrame *frame)
708 {
709   MetaFrameGeometry fgeom;
710   meta_ui_frame_calc_geometry (frame, &fgeom);
711   return get_visible_region (frame, &fgeom);
712 }
713 
714 void
meta_ui_frame_move_resize(MetaUIFrame * frame,int x,int y,int width,int height)715 meta_ui_frame_move_resize (MetaUIFrame *frame,
716                            int x, int y, int width, int height)
717 {
718   int old_width, old_height;
719 
720   old_width = gdk_window_get_width (frame->window);
721   old_height = gdk_window_get_height (frame->window);
722 
723   gdk_window_move_resize (frame->window, x, y, width, height);
724 
725   if (old_width != width || old_height != height)
726     invalidate_whole_window (frame);
727 }
728 
729 void
meta_ui_frame_queue_draw(MetaUIFrame * frame)730 meta_ui_frame_queue_draw (MetaUIFrame *frame)
731 {
732   invalidate_whole_window (frame);
733 }
734 
735 void
meta_ui_frame_set_title(MetaUIFrame * frame,const char * title)736 meta_ui_frame_set_title (MetaUIFrame *frame,
737                          const char *title)
738 {
739   g_free (frame->title);
740   frame->title = g_strdup (title);
741 
742   g_clear_object (&frame->text_layout);
743 
744   invalidate_whole_window (frame);
745 }
746 
747 void
meta_ui_frame_update_style(MetaUIFrame * frame)748 meta_ui_frame_update_style (MetaUIFrame *frame)
749 {
750   meta_ui_frame_attach_style (frame);
751   invalidate_whole_window (frame);
752 }
753 
754 static void
redraw_control(MetaUIFrame * frame,MetaFrameControl control)755 redraw_control (MetaUIFrame *frame,
756                 MetaFrameControl control)
757 {
758   MetaFrameGeometry fgeom;
759   GdkRectangle *rect;
760 
761   meta_ui_frame_calc_geometry (frame, &fgeom);
762 
763   rect = control_rect (control, &fgeom);
764 
765   gdk_window_invalidate_rect (frame->window, rect, FALSE);
766 }
767 
768 static gboolean
meta_frame_titlebar_event(MetaUIFrame * frame,const ClutterEvent * event,int action)769 meta_frame_titlebar_event (MetaUIFrame        *frame,
770                            const ClutterEvent *event,
771                            int                 action)
772 {
773   MetaFrameFlags flags;
774   MetaX11Display *x11_display;
775   uint32_t evtime;
776   float x, y;
777 
778   g_assert (event->type == CLUTTER_BUTTON_PRESS ||
779             event->type == CLUTTER_TOUCH_BEGIN);
780 
781   x11_display = frame->frames->x11_display;
782 
783   flags = meta_frame_get_flags (frame->meta_window->frame);
784 
785   evtime = clutter_event_get_time (event);
786   clutter_event_get_coords (event, &x, &y);
787 
788   switch (action)
789     {
790     case G_DESKTOP_TITLEBAR_ACTION_TOGGLE_SHADE:
791       {
792         if (flags & META_FRAME_ALLOWS_SHADE)
793           {
794             if (flags & META_FRAME_SHADED)
795               meta_window_unshade (frame->meta_window, evtime);
796             else
797               meta_window_shade (frame->meta_window, evtime);
798           }
799       }
800       break;
801 
802     case G_DESKTOP_TITLEBAR_ACTION_TOGGLE_MAXIMIZE:
803       {
804         if (flags & META_FRAME_ALLOWS_MAXIMIZE)
805           {
806             meta_x11_wm_toggle_maximize (x11_display, frame->xwindow);
807           }
808       }
809       break;
810 
811     case G_DESKTOP_TITLEBAR_ACTION_TOGGLE_MAXIMIZE_HORIZONTALLY:
812       {
813         if (flags & META_FRAME_ALLOWS_MAXIMIZE)
814           {
815             meta_x11_wm_toggle_maximize_horizontally (x11_display,
816                                                       frame->xwindow);
817           }
818       }
819       break;
820 
821     case G_DESKTOP_TITLEBAR_ACTION_TOGGLE_MAXIMIZE_VERTICALLY:
822       {
823         if (flags & META_FRAME_ALLOWS_MAXIMIZE)
824           {
825             meta_x11_wm_toggle_maximize_vertically (x11_display, frame->xwindow);
826           }
827       }
828       break;
829 
830     case G_DESKTOP_TITLEBAR_ACTION_MINIMIZE:
831       {
832         if (flags & META_FRAME_ALLOWS_MINIMIZE)
833           meta_window_minimize (frame->meta_window);
834       }
835       break;
836 
837     case G_DESKTOP_TITLEBAR_ACTION_NONE:
838       /* Yaay, a sane user that doesn't use that other weird crap! */
839       break;
840 
841     case G_DESKTOP_TITLEBAR_ACTION_LOWER:
842       meta_x11_wm_user_lower_and_unfocus (x11_display,
843                                           frame->xwindow,
844                                           evtime);
845       break;
846 
847     case G_DESKTOP_TITLEBAR_ACTION_MENU:
848       meta_x11_wm_show_window_menu (x11_display,
849                                     frame->xwindow,
850                                     META_WINDOW_MENU_WM,
851                                     x, y, evtime);
852       break;
853     }
854 
855   return TRUE;
856 }
857 
858 static gboolean
meta_frame_double_click_event(MetaUIFrame * frame,const ClutterEvent * event)859 meta_frame_double_click_event (MetaUIFrame        *frame,
860                                const ClutterEvent *event)
861 {
862   int action = meta_prefs_get_action_double_click_titlebar ();
863 
864   return meta_frame_titlebar_event (frame, event, action);
865 }
866 
867 static gboolean
meta_frame_middle_click_event(MetaUIFrame * frame,ClutterButtonEvent * event)868 meta_frame_middle_click_event (MetaUIFrame *frame,
869                                ClutterButtonEvent *event)
870 {
871   int action = meta_prefs_get_action_middle_click_titlebar();
872 
873   return meta_frame_titlebar_event (frame, (const ClutterEvent *) event,
874                                     action);
875 }
876 
877 static gboolean
meta_frame_right_click_event(MetaUIFrame * frame,ClutterButtonEvent * event)878 meta_frame_right_click_event (MetaUIFrame *frame,
879                               ClutterButtonEvent *event)
880 {
881   int action = meta_prefs_get_action_right_click_titlebar();
882 
883   return meta_frame_titlebar_event (frame, (const ClutterEvent *) event,
884                                     action);
885 }
886 
887 static gboolean
meta_frames_try_grab_op(MetaUIFrame * frame,MetaGrabOp op,gdouble grab_x,gdouble grab_y,guint32 time)888 meta_frames_try_grab_op (MetaUIFrame *frame,
889                          MetaGrabOp   op,
890                          gdouble      grab_x,
891                          gdouble      grab_y,
892                          guint32      time)
893 {
894   MetaFrames *frames = frame->frames;
895   gboolean ret;
896 
897   ret = meta_x11_wm_begin_grab_op (frames->x11_display,
898                                    frame->xwindow,
899                                    op,
900                                    FALSE,
901                                    TRUE,
902                                    frame->grab_button,
903                                    0,
904                                    time,
905                                    grab_x, grab_y);
906   if (!ret)
907     {
908       frames->current_grab_op = op;
909       frames->grab_frame = frame;
910       frames->grab_x = grab_x;
911       frames->grab_y = grab_y;
912     }
913   else
914     frames->grab_touch = NULL;
915 
916   return ret;
917 }
918 
919 static gboolean
meta_frames_retry_grab_op(MetaFrames * frames,guint time)920 meta_frames_retry_grab_op (MetaFrames *frames,
921                            guint       time)
922 {
923   MetaGrabOp op;
924   gboolean ret;
925 
926   if (frames->current_grab_op == META_GRAB_OP_NONE)
927     return TRUE;
928 
929   op = frames->current_grab_op;
930   frames->current_grab_op = META_GRAB_OP_NONE;
931 
932   ret = meta_x11_wm_begin_grab_op (frames->x11_display,
933                                    frames->grab_frame->xwindow,
934                                    op,
935                                    FALSE,
936                                    TRUE,
937                                    frames->grab_frame->grab_button,
938                                    0,
939                                    time,
940                                    frames->grab_x,
941                                    frames->grab_y);
942   if (ret)
943     frames->grab_touch = NULL;
944 
945   return ret;
946 }
947 
948 static MetaGrabOp
grab_op_from_resize_control(MetaFrameControl control)949 grab_op_from_resize_control (MetaFrameControl control)
950 {
951   switch (control)
952     {
953     case META_FRAME_CONTROL_RESIZE_SE:
954       return META_GRAB_OP_RESIZING_SE;
955     case META_FRAME_CONTROL_RESIZE_S:
956       return META_GRAB_OP_RESIZING_S;
957     case META_FRAME_CONTROL_RESIZE_SW:
958       return META_GRAB_OP_RESIZING_SW;
959     case META_FRAME_CONTROL_RESIZE_NE:
960       return META_GRAB_OP_RESIZING_NE;
961     case META_FRAME_CONTROL_RESIZE_N:
962       return META_GRAB_OP_RESIZING_N;
963     case META_FRAME_CONTROL_RESIZE_NW:
964       return META_GRAB_OP_RESIZING_NW;
965     case META_FRAME_CONTROL_RESIZE_E:
966       return META_GRAB_OP_RESIZING_E;
967     case META_FRAME_CONTROL_RESIZE_W:
968       return META_GRAB_OP_RESIZING_W;
969     default:
970       g_assert_not_reached ();
971       return META_GRAB_OP_NONE;
972     }
973 }
974 
975 static guint
get_action(const ClutterEvent * event)976 get_action (const ClutterEvent *event)
977 {
978   if (event->type == CLUTTER_BUTTON_PRESS ||
979       event->type == CLUTTER_BUTTON_RELEASE)
980     {
981       switch (event->button.button)
982         {
983         case CLUTTER_BUTTON_PRIMARY:
984           if (clutter_event_get_click_count (event) == 2)
985             return META_ACTION_DOUBLE_CLICK;
986           else
987             return META_ACTION_CLICK;
988         case CLUTTER_BUTTON_SECONDARY:
989           return META_ACTION_RIGHT_CLICK;
990         case CLUTTER_BUTTON_MIDDLE:
991           return META_ACTION_MIDDLE_CLICK;
992         default:
993           meta_verbose ("No action triggered for button %u %s",
994                         event->button.button,
995                         (event->type == CLUTTER_BUTTON_PRESS) ? "press" : "release");
996         }
997     }
998   else if (event->type == CLUTTER_TOUCH_BEGIN ||
999            event->type == CLUTTER_TOUCH_UPDATE ||
1000            event->type == CLUTTER_TOUCH_END)
1001     {
1002       return META_ACTION_CLICK;
1003     }
1004 
1005   return META_ACTION_IGNORE;
1006 }
1007 
1008 static uint32_t
get_button_number(const ClutterEvent * event)1009 get_button_number (const ClutterEvent *event)
1010 {
1011   if (event->type == CLUTTER_TOUCH_BEGIN ||
1012       event->type == CLUTTER_TOUCH_UPDATE ||
1013       event->type == CLUTTER_TOUCH_END)
1014     return -1;
1015   else if (event->type == CLUTTER_BUTTON_PRESS ||
1016            event->type == CLUTTER_BUTTON_RELEASE)
1017     return clutter_event_get_button (event);
1018 
1019   g_assert_not_reached ();
1020   return -1;
1021 }
1022 
1023 static gboolean
meta_frame_left_click_event(MetaUIFrame * frame,const ClutterEvent * event)1024 meta_frame_left_click_event (MetaUIFrame        *frame,
1025                              const ClutterEvent *event)
1026 {
1027   MetaX11Display *x11_display = frame->frames->x11_display;
1028   MetaFrameControl control;
1029   guint32 evtime;
1030   gfloat x, y;
1031 
1032   evtime = clutter_event_get_time (event);
1033   clutter_event_get_coords (event, &x, &y);
1034   control = get_control (frame, x, y);
1035 
1036   switch (control)
1037     {
1038     case META_FRAME_CONTROL_MAXIMIZE:
1039     case META_FRAME_CONTROL_UNMAXIMIZE:
1040     case META_FRAME_CONTROL_MINIMIZE:
1041     case META_FRAME_CONTROL_DELETE:
1042     case META_FRAME_CONTROL_MENU:
1043       frame->grab_button = get_button_number (event);
1044       frame->button_state = META_BUTTON_STATE_PRESSED;
1045       frame->prelit_control = control;
1046       redraw_control (frame, control);
1047 
1048       if (control == META_FRAME_CONTROL_MENU)
1049         {
1050           MetaFrameGeometry fgeom;
1051           GdkRectangle *rect;
1052           MetaRectangle root_rect;
1053           int win_x, win_y;
1054 
1055           meta_ui_frame_calc_geometry (frame, &fgeom);
1056 
1057           rect = control_rect (control, &fgeom);
1058 
1059           gdk_window_get_position (frame->window, &win_x, &win_y);
1060 
1061           root_rect.x = win_x + rect->x;
1062           root_rect.y = win_y + rect->y;
1063           root_rect.width = rect->width;
1064           root_rect.height = rect->height;
1065 
1066           /* if the compositor takes a grab for showing the menu, we will
1067            * get a LeaveNotify event we want to ignore, to keep the pressed
1068            * button state while the menu is open
1069            */
1070           frame->maybe_ignore_leave_notify = TRUE;
1071           meta_x11_wm_show_window_menu_for_rect (x11_display,
1072                                                  frame->xwindow,
1073                                                  META_WINDOW_MENU_WM,
1074                                                  &root_rect,
1075                                                  evtime);
1076         }
1077       else
1078         {
1079           meta_frames_try_grab_op (frame, META_GRAB_OP_FRAME_BUTTON,
1080                                    x, y, evtime);
1081         }
1082 
1083       return TRUE;
1084     case META_FRAME_CONTROL_RESIZE_SE:
1085     case META_FRAME_CONTROL_RESIZE_S:
1086     case META_FRAME_CONTROL_RESIZE_SW:
1087     case META_FRAME_CONTROL_RESIZE_NE:
1088     case META_FRAME_CONTROL_RESIZE_N:
1089     case META_FRAME_CONTROL_RESIZE_NW:
1090     case META_FRAME_CONTROL_RESIZE_E:
1091     case META_FRAME_CONTROL_RESIZE_W:
1092       meta_frames_try_grab_op (frame,
1093                                grab_op_from_resize_control (control),
1094                                x, y, evtime);
1095 
1096       return TRUE;
1097     case META_FRAME_CONTROL_TITLE:
1098       {
1099         MetaFrameFlags flags = meta_frame_get_flags (frame->meta_window->frame);
1100 
1101         if (flags & META_FRAME_ALLOWS_MOVE)
1102           {
1103             meta_frames_try_grab_op (frame,
1104                                      META_GRAB_OP_MOVING,
1105                                      x, y, evtime);
1106           }
1107       }
1108 
1109       return TRUE;
1110     case META_FRAME_CONTROL_NONE:
1111       /* We can get this for example when trying to resize window
1112        * that cannot be resized (e. g. it is maximized and the theme
1113        * currently used has borders for maximized windows), see #751884 */
1114       return FALSE;
1115     case META_FRAME_CONTROL_CLIENT_AREA:
1116       /* This can happen with broken gtk themes that have a larger shadow size
1117        * in the unfocused state than in the focused one. Then when clicking
1118        * below the titlebar area in the unfocused state would still be
1119        * considered a click on the titlebar due to it being shifted down because
1120        * of the shadow. This then causes the window to be focused before this
1121        * function is called, which removes the shadow such that the same
1122        * position is now considered to be on the client area */
1123       return FALSE;
1124     default:
1125       g_assert_not_reached ();
1126       return FALSE;
1127     }
1128 }
1129 
1130 static gboolean
handle_press_event(MetaUIFrame * frame,const ClutterEvent * event)1131 handle_press_event (MetaUIFrame        *frame,
1132                     const ClutterEvent *event)
1133 {
1134   MetaFrameControl control;
1135   uint32_t evtime, action;
1136   float x, y;
1137 
1138   g_assert (event->type == CLUTTER_BUTTON_PRESS ||
1139             event->type == CLUTTER_TOUCH_BEGIN);
1140 
1141   action = get_action (event);
1142   if (action == META_ACTION_IGNORE)
1143     return FALSE;
1144 
1145   evtime = clutter_event_get_time (event);
1146   clutter_event_get_coords (event, &x, &y);
1147   control = get_control (frame, x, y);
1148   /* don't do the rest of this if on client area */
1149   if (control == META_FRAME_CONTROL_CLIENT_AREA)
1150     return FALSE; /* not on the frame, just passed through from client */
1151 
1152   if (action == META_ACTION_CLICK &&
1153       !(control == META_FRAME_CONTROL_MINIMIZE ||
1154         control == META_FRAME_CONTROL_DELETE ||
1155         control == META_FRAME_CONTROL_MAXIMIZE))
1156     {
1157       meta_topic (META_DEBUG_FOCUS,
1158                   "Focusing window with frame 0x%lx due to button 1 press",
1159                   frame->xwindow);
1160       meta_window_focus (frame->meta_window, evtime);
1161     }
1162 
1163   /* We want to shade even if we have a GrabOp, since we'll have a move grab
1164    * if we double click the titlebar.
1165    */
1166   if (control == META_FRAME_CONTROL_TITLE &&
1167       action == META_ACTION_DOUBLE_CLICK)
1168     {
1169       meta_x11_wm_end_grab_op (frame->frames->x11_display, evtime);
1170       return meta_frame_double_click_event (frame, event);
1171     }
1172 
1173   if (meta_x11_wm_get_grab_op (frame->frames->x11_display) != META_GRAB_OP_NONE)
1174     return FALSE; /* already up to something */
1175 
1176   frame->grab_button = get_button_number (event);
1177 
1178   switch (action)
1179     {
1180     case META_ACTION_CLICK:
1181       return meta_frame_left_click_event (frame, event);
1182     case META_ACTION_MIDDLE_CLICK:
1183       return meta_frame_middle_click_event (frame, (ClutterButtonEvent *) event);
1184     case META_ACTION_RIGHT_CLICK:
1185       return meta_frame_right_click_event (frame, (ClutterButtonEvent *) event);
1186     default:
1187       return FALSE;
1188     }
1189 }
1190 
1191 static gboolean
handle_release_event(MetaUIFrame * frame,const ClutterEvent * event)1192 handle_release_event (MetaUIFrame        *frame,
1193                       const ClutterEvent *event)
1194 {
1195   guint32 evtime, button;
1196   gfloat x, y;
1197 
1198   g_assert (event->type == CLUTTER_BUTTON_RELEASE ||
1199             event->type == CLUTTER_TOUCH_END);
1200 
1201   evtime = clutter_event_get_time (event);
1202   clutter_event_get_coords (event, &x, &y);
1203   button = get_button_number (event);
1204 
1205   frame->frames->current_grab_op = META_GRAB_OP_NONE;
1206   meta_x11_wm_end_grab_op (frame->frames->x11_display, evtime);
1207 
1208   /* We only handle the releases we handled the presses for (things
1209    * involving frame controls). Window ops that don't require a
1210    * frame are handled in the Xlib part of the code, display.c/window.c
1211    */
1212   if (((int) button) == frame->grab_button &&
1213       frame->button_state == META_BUTTON_STATE_PRESSED)
1214     {
1215       switch (frame->prelit_control)
1216         {
1217         case META_FRAME_CONTROL_MINIMIZE:
1218           meta_window_minimize (frame->meta_window);
1219           break;
1220         case META_FRAME_CONTROL_MAXIMIZE:
1221           /* Focus the window on the maximize */
1222           meta_window_focus (frame->meta_window, evtime);
1223           if (meta_prefs_get_raise_on_click ())
1224             meta_window_raise (frame->meta_window);
1225           meta_window_maximize (frame->meta_window, META_MAXIMIZE_BOTH);
1226           break;
1227         case META_FRAME_CONTROL_UNMAXIMIZE:
1228           if (meta_prefs_get_raise_on_click ())
1229             meta_window_raise (frame->meta_window);
1230           meta_window_unmaximize (frame->meta_window, META_MAXIMIZE_BOTH);
1231           break;
1232         case META_FRAME_CONTROL_DELETE:
1233           meta_window_delete (frame->meta_window, evtime);
1234           break;
1235         default:
1236           break;
1237         }
1238 
1239       /* Update the prelit control regardless of what button the mouse
1240        * was released over; needed so that the new button can become
1241        * prelit so to let the user know that it can now be pressed.
1242        * :)
1243        */
1244       MetaFrameControl control = get_control (frame, x, y);
1245       meta_ui_frame_update_prelit_control (frame, control);
1246     }
1247 
1248   return TRUE;
1249 }
1250 
1251 static void
meta_ui_frame_update_prelit_control(MetaUIFrame * frame,MetaFrameControl control)1252 meta_ui_frame_update_prelit_control (MetaUIFrame     *frame,
1253                                      MetaFrameControl control)
1254 {
1255   MetaFrameControl old_control;
1256   MetaCursor cursor;
1257 
1258   meta_verbose ("Updating prelit control from %u to %u",
1259                 frame->prelit_control, control);
1260 
1261   cursor = META_CURSOR_DEFAULT;
1262 
1263   switch (control)
1264     {
1265     case META_FRAME_CONTROL_CLIENT_AREA:
1266       break;
1267     case META_FRAME_CONTROL_NONE:
1268       break;
1269     case META_FRAME_CONTROL_TITLE:
1270       break;
1271     case META_FRAME_CONTROL_DELETE:
1272       break;
1273     case META_FRAME_CONTROL_MENU:
1274       break;
1275     case META_FRAME_CONTROL_MINIMIZE:
1276       break;
1277     case META_FRAME_CONTROL_MAXIMIZE:
1278       break;
1279     case META_FRAME_CONTROL_UNMAXIMIZE:
1280       break;
1281     case META_FRAME_CONTROL_RESIZE_SE:
1282       cursor = META_CURSOR_SE_RESIZE;
1283       break;
1284     case META_FRAME_CONTROL_RESIZE_S:
1285       cursor = META_CURSOR_SOUTH_RESIZE;
1286       break;
1287     case META_FRAME_CONTROL_RESIZE_SW:
1288       cursor = META_CURSOR_SW_RESIZE;
1289       break;
1290     case META_FRAME_CONTROL_RESIZE_N:
1291       cursor = META_CURSOR_NORTH_RESIZE;
1292       break;
1293     case META_FRAME_CONTROL_RESIZE_NE:
1294       cursor = META_CURSOR_NE_RESIZE;
1295       break;
1296     case META_FRAME_CONTROL_RESIZE_NW:
1297       cursor = META_CURSOR_NW_RESIZE;
1298       break;
1299     case META_FRAME_CONTROL_RESIZE_W:
1300       cursor = META_CURSOR_WEST_RESIZE;
1301       break;
1302     case META_FRAME_CONTROL_RESIZE_E:
1303       cursor = META_CURSOR_EAST_RESIZE;
1304       break;
1305     }
1306 
1307   /* set/unset the prelight cursor */
1308   meta_x11_wm_set_screen_cursor (frame->frames->x11_display,
1309                                  frame->xwindow,
1310                                  cursor);
1311 
1312   switch (control)
1313     {
1314     case META_FRAME_CONTROL_MENU:
1315     case META_FRAME_CONTROL_MINIMIZE:
1316     case META_FRAME_CONTROL_MAXIMIZE:
1317     case META_FRAME_CONTROL_DELETE:
1318     case META_FRAME_CONTROL_UNMAXIMIZE:
1319       /* leave control set */
1320       break;
1321     default:
1322       /* Only prelight buttons */
1323       control = META_FRAME_CONTROL_NONE;
1324       break;
1325     }
1326 
1327   if (control == frame->prelit_control &&
1328       frame->button_state == META_BUTTON_STATE_PRELIGHT)
1329     return;
1330 
1331   /* Save the old control so we can unprelight it */
1332   old_control = frame->prelit_control;
1333 
1334   frame->button_state = META_BUTTON_STATE_PRELIGHT;
1335   frame->prelit_control = control;
1336 
1337   redraw_control (frame, old_control);
1338   redraw_control (frame, control);
1339 }
1340 
1341 static gboolean
handle_motion_event(MetaUIFrame * frame,const ClutterEvent * event)1342 handle_motion_event (MetaUIFrame        *frame,
1343                      const ClutterEvent *event)
1344 {
1345   MetaFrames *frames = frame->frames;
1346   MetaFrameControl control;
1347   ClutterModifierType modifiers;
1348   guint32 evtime;
1349   gfloat x, y;
1350 
1351   g_assert (event->type == CLUTTER_MOTION ||
1352             event->type == CLUTTER_TOUCH_UPDATE);
1353 
1354   modifiers = clutter_event_get_state (event);
1355   evtime = clutter_event_get_time (event);
1356   clutter_event_get_coords (event, &x, &y);
1357   control = get_control (frame, x, y);
1358 
1359   if (frame->button_state == META_BUTTON_STATE_PRESSED)
1360     {
1361       /* If the user leaves the frame button, set the state
1362        * back to normal and redraw. */
1363       if (frame->prelit_control != control)
1364         {
1365           frame->button_state = META_BUTTON_STATE_NORMAL;
1366           redraw_control (frame, frame->prelit_control);
1367         }
1368     }
1369   else
1370     {
1371       /* Update prelit control and cursor */
1372       meta_ui_frame_update_prelit_control (frame, control);
1373     }
1374 
1375   if (frames->current_grab_op != META_GRAB_OP_NONE &&
1376       (event->type == CLUTTER_TOUCH_UPDATE ||
1377        (event->type == CLUTTER_MOTION &&
1378         (modifiers & CLUTTER_BUTTON1_MASK))))
1379     meta_frames_retry_grab_op (frames, evtime);
1380 
1381   return TRUE;
1382 }
1383 
1384 static cairo_region_t *
get_visible_frame_border_region(MetaUIFrame * frame)1385 get_visible_frame_border_region (MetaUIFrame *frame)
1386 {
1387   cairo_rectangle_int_t area;
1388   cairo_region_t *frame_border;
1389   MetaFrameFlags flags;
1390   MetaFrameType type;
1391   MetaFrameBorders borders;
1392   MetaRectangle buffer_rect = frame->meta_window->buffer_rect;
1393 
1394   flags = meta_frame_get_flags (frame->meta_window->frame);
1395   type = meta_window_get_frame_type (frame->meta_window);
1396 
1397   meta_theme_get_frame_borders (meta_theme_get_default (), frame->style_info,
1398                                 type, frame->text_height, flags,
1399                                 &borders);
1400 
1401   /* Frame rect */
1402   area.x = 0;
1403   area.y = 0;
1404   area.width = buffer_rect.width;
1405   area.height = buffer_rect.height;
1406 
1407   frame_border = cairo_region_create_rectangle (&area);
1408 
1409   /* Client rect */
1410   area.x += borders.total.left;
1411   area.y += borders.total.top;
1412   area.width -= borders.total.left + borders.total.right;
1413   area.height -= borders.total.top + borders.total.bottom;
1414 
1415   /* Visible frame border */
1416   cairo_region_subtract_rectangle (frame_border, &area);
1417   return frame_border;
1418 }
1419 
1420 /*
1421  * Draw the opaque and semi-opaque pixels of this frame into a mask.
1422  *
1423  * (0,0) in Cairo coordinates is assumed to be the top left corner of the
1424  * invisible border.
1425  *
1426  * The parts of @cr's surface in the clip region are assumed to be
1427  * initialized to fully-transparent, and the clip region is assumed to
1428  * contain the invisible border and the visible parts of the frame, but
1429  * not the client area.
1430  *
1431  * This function uses @cr to draw pixels of arbitrary color (it will
1432  * typically be drawing in a %CAIRO_FORMAT_A8 surface, so the color is
1433  * discarded anyway) with appropriate alpha values to reproduce this
1434  * frame's alpha channel, as a mask to be applied to an opaque pixmap.
1435  *
1436  * @frame: This frame
1437  * @frame_rect: The frame rect
1438  * @cr: Used to draw the resulting mask
1439  */
1440 void
meta_ui_frame_get_mask(MetaUIFrame * frame,cairo_rectangle_int_t * frame_rect,cairo_t * cr)1441 meta_ui_frame_get_mask (MetaUIFrame           *frame,
1442                         cairo_rectangle_int_t *frame_rect,
1443                         cairo_t               *cr)
1444 {
1445   MetaFrameBorders borders;
1446   MetaFrameFlags flags;
1447   cairo_surface_t *surface;
1448   double xscale, yscale;
1449   int scale;
1450 
1451   flags = meta_frame_get_flags (frame->meta_window->frame);
1452 
1453   meta_style_info_set_flags (frame->style_info, flags);
1454   meta_ui_frame_get_borders (frame, &borders);
1455 
1456   /* See comment in meta_frame_layout_draw_with_style() for details on HiDPI handling */
1457   scale = meta_theme_get_window_scaling_factor ();
1458   surface = cairo_get_target (cr);
1459   cairo_surface_get_device_scale (surface, &xscale, &yscale);
1460   cairo_surface_set_device_scale (surface, scale, scale);
1461 
1462   gtk_render_background (frame->style_info->styles[META_STYLE_ELEMENT_FRAME], cr,
1463                          borders.invisible.left / scale,
1464                          borders.invisible.top / scale,
1465                          frame_rect->width / scale, frame_rect->height / scale);
1466   gtk_render_background (frame->style_info->styles[META_STYLE_ELEMENT_TITLEBAR], cr,
1467                          borders.invisible.left / scale,
1468                          borders.invisible.top / scale,
1469                          frame_rect->width / scale, borders.total.top / scale);
1470 
1471   cairo_surface_set_device_scale (surface, xscale, yscale);
1472 }
1473 
1474 /* XXX -- this is disgusting. Find a better approach here.
1475  * Use multiple widgets? */
1476 static MetaUIFrame *
find_frame_to_draw(MetaFrames * frames,cairo_t * cr)1477 find_frame_to_draw (MetaFrames *frames,
1478                     cairo_t    *cr)
1479 {
1480   GHashTableIter iter;
1481   MetaUIFrame *frame;
1482 
1483   g_hash_table_iter_init (&iter, frames->frames);
1484   while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &frame))
1485     if (gtk_cairo_should_draw_window (cr, frame->window))
1486       return frame;
1487 
1488   return NULL;
1489 }
1490 
1491 static gboolean
meta_frames_draw(GtkWidget * widget,cairo_t * cr)1492 meta_frames_draw (GtkWidget *widget,
1493                   cairo_t   *cr)
1494 {
1495   MetaUIFrame *frame;
1496   MetaFrames *frames;
1497   cairo_region_t *region;
1498 
1499   frames = META_FRAMES (widget);
1500 
1501   frame = find_frame_to_draw (frames, cr);
1502   if (frame == NULL)
1503     return FALSE;
1504 
1505   region = get_visible_frame_border_region (frame);
1506   gdk_cairo_region (cr, region);
1507   cairo_clip (cr);
1508 
1509   /* The target may be cleared to black or transparent, depending
1510    * on the frame's visual; we don't want decorations to appear
1511    * differently when the theme's decorations aren't fully opaque,
1512    * so clear to black first
1513    */
1514   cairo_paint (cr);
1515 
1516   meta_ui_frame_paint (frame, cr);
1517   cairo_region_destroy (region);
1518 
1519   return TRUE;
1520 }
1521 
1522 static void
meta_ui_frame_paint(MetaUIFrame * frame,cairo_t * cr)1523 meta_ui_frame_paint (MetaUIFrame  *frame,
1524                      cairo_t      *cr)
1525 {
1526   MetaFrameFlags flags;
1527   MetaFrameType type;
1528   cairo_surface_t *mini_icon;
1529   MetaButtonState button_states[META_BUTTON_TYPE_LAST];
1530   int i;
1531   int button_type = -1;
1532   MetaButtonLayout button_layout;
1533   MetaWindowX11 *window_x11 = META_WINDOW_X11 (frame->meta_window);
1534   MetaRectangle client_rect;
1535 
1536   for (i = 0; i < META_BUTTON_TYPE_LAST; i++)
1537     button_states[i] = META_BUTTON_STATE_NORMAL;
1538 
1539   /* Set prelight state */
1540   switch (frame->prelit_control)
1541     {
1542     case META_FRAME_CONTROL_MENU:
1543       button_type = META_BUTTON_TYPE_MENU;
1544       break;
1545     case META_FRAME_CONTROL_MINIMIZE:
1546       button_type = META_BUTTON_TYPE_MINIMIZE;
1547       break;
1548     case META_FRAME_CONTROL_MAXIMIZE:
1549       button_type = META_BUTTON_TYPE_MAXIMIZE;
1550       break;
1551     case META_FRAME_CONTROL_UNMAXIMIZE:
1552       button_type = META_BUTTON_TYPE_MAXIMIZE;
1553       break;
1554     case META_FRAME_CONTROL_DELETE:
1555       button_type = META_BUTTON_TYPE_CLOSE;
1556       break;
1557     default:
1558       break;
1559     }
1560 
1561   if (button_type > -1)
1562     button_states[button_type] = frame->button_state;
1563 
1564   mini_icon = frame->meta_window->mini_icon;
1565   flags = meta_frame_get_flags (frame->meta_window->frame);
1566   type = meta_window_get_frame_type (frame->meta_window);
1567 
1568   meta_ui_frame_ensure_layout (frame, type);
1569 
1570   meta_prefs_get_button_layout (&button_layout);
1571 
1572   client_rect = meta_window_x11_get_client_rect (window_x11);
1573 
1574   meta_theme_draw_frame (meta_theme_get_default (),
1575                          frame->style_info,
1576                          cr,
1577                          type,
1578                          flags,
1579                          client_rect.width,
1580                          client_rect.height,
1581                          frame->text_layout,
1582                          frame->text_height,
1583                          &button_layout,
1584                          button_states,
1585                          mini_icon);
1586 
1587   if (frame->is_frozen)
1588     {
1589       meta_window_x11_thaw_commits (frame->meta_window);
1590       frame->is_frozen = FALSE;
1591     }
1592 }
1593 
1594 static gboolean
handle_enter_notify_event(MetaUIFrame * frame,ClutterCrossingEvent * event)1595 handle_enter_notify_event (MetaUIFrame *frame,
1596                            ClutterCrossingEvent *event)
1597 {
1598   MetaFrameControl control;
1599 
1600   frame->maybe_ignore_leave_notify = FALSE;
1601 
1602   control = get_control (frame, event->x, event->y);
1603   meta_ui_frame_update_prelit_control (frame, control);
1604 
1605   return TRUE;
1606 }
1607 
1608 static gboolean
handle_leave_notify_event(MetaUIFrame * frame,ClutterCrossingEvent * event)1609 handle_leave_notify_event (MetaUIFrame *frame,
1610                            ClutterCrossingEvent *event)
1611 {
1612   MetaGrabOp grab_op;
1613 
1614   grab_op = meta_x11_wm_get_grab_op (frame->frames->x11_display);
1615 
1616   /* ignore the first LeaveNotify event after opening a window menu
1617    * if it is the result of a compositor grab
1618    */
1619   frame->maybe_ignore_leave_notify = frame->maybe_ignore_leave_notify &&
1620                                      grab_op == META_GRAB_OP_COMPOSITOR;
1621 
1622   if (frame->maybe_ignore_leave_notify)
1623     return FALSE;
1624 
1625   meta_ui_frame_update_prelit_control (frame, META_FRAME_CONTROL_NONE);
1626 
1627   return TRUE;
1628 }
1629 
1630 gboolean
meta_ui_frame_handle_event(MetaUIFrame * frame,const ClutterEvent * event)1631 meta_ui_frame_handle_event (MetaUIFrame *frame,
1632                             const ClutterEvent *event)
1633 {
1634   if (event->type == CLUTTER_TOUCH_BEGIN ||
1635       event->type == CLUTTER_TOUCH_UPDATE ||
1636       event->type == CLUTTER_TOUCH_END)
1637     {
1638       ClutterEventSequence *sequence;
1639       MetaFrames *frames = frame->frames;
1640 
1641       /* In X11, mutter sets up passive touch grabs which basically
1642        * means we handle those events twice (once through the passive
1643        * grab, and then through XISelectEvents).
1644        *
1645        * Receiving touch events here means we are going through the
1646        * former, but passive grabs are exclusively for gesture
1647        * recognition purposes.
1648        *
1649        * We do actually want this to happen though the regular event
1650        * selection paths to avoid breaking internal state, which means
1651        * we will get pointer events, because we don't select for XI_Touch*.
1652        */
1653       if (!meta_is_wayland_compositor ())
1654         return FALSE;
1655 
1656       sequence = clutter_event_get_event_sequence (event);
1657 
1658       /* Lock onto a single touch */
1659       if (frames->grab_touch && frames->grab_touch != sequence)
1660         return FALSE;
1661 
1662       if (event->type == CLUTTER_TOUCH_BEGIN)
1663         frames->grab_touch = sequence;
1664       else if (event->type == CLUTTER_TOUCH_END)
1665         frames->grab_touch = NULL;
1666     }
1667 
1668   switch (event->any.type)
1669     {
1670     case CLUTTER_BUTTON_PRESS:
1671     case CLUTTER_TOUCH_BEGIN:
1672       return handle_press_event (frame, event);
1673     case CLUTTER_BUTTON_RELEASE:
1674     case CLUTTER_TOUCH_END:
1675       return handle_release_event (frame, event);
1676     case CLUTTER_MOTION:
1677     case CLUTTER_TOUCH_UPDATE:
1678       return handle_motion_event (frame, event);
1679     case CLUTTER_ENTER:
1680       return handle_enter_notify_event (frame, (ClutterCrossingEvent *) event);
1681     case CLUTTER_LEAVE:
1682       return handle_leave_notify_event (frame, (ClutterCrossingEvent *) event);
1683     default:
1684       return FALSE;
1685     }
1686 }
1687 
1688 static GdkRectangle*
control_rect(MetaFrameControl control,MetaFrameGeometry * fgeom)1689 control_rect (MetaFrameControl control,
1690               MetaFrameGeometry *fgeom)
1691 {
1692   GdkRectangle *rect;
1693 
1694   rect = NULL;
1695   switch (control)
1696     {
1697     case META_FRAME_CONTROL_TITLE:
1698       rect = &fgeom->title_rect;
1699       break;
1700     case META_FRAME_CONTROL_DELETE:
1701       rect = &fgeom->close_rect.visible;
1702       break;
1703     case META_FRAME_CONTROL_MENU:
1704       rect = &fgeom->menu_rect.visible;
1705       break;
1706     case META_FRAME_CONTROL_MINIMIZE:
1707       rect = &fgeom->min_rect.visible;
1708       break;
1709     case META_FRAME_CONTROL_MAXIMIZE:
1710     case META_FRAME_CONTROL_UNMAXIMIZE:
1711       rect = &fgeom->max_rect.visible;
1712       break;
1713     case META_FRAME_CONTROL_RESIZE_SE:
1714       break;
1715     case META_FRAME_CONTROL_RESIZE_S:
1716       break;
1717     case META_FRAME_CONTROL_RESIZE_SW:
1718       break;
1719     case META_FRAME_CONTROL_RESIZE_N:
1720       break;
1721     case META_FRAME_CONTROL_RESIZE_NE:
1722       break;
1723     case META_FRAME_CONTROL_RESIZE_NW:
1724       break;
1725     case META_FRAME_CONTROL_RESIZE_W:
1726       break;
1727     case META_FRAME_CONTROL_RESIZE_E:
1728       break;
1729     case META_FRAME_CONTROL_NONE:
1730       break;
1731     case META_FRAME_CONTROL_CLIENT_AREA:
1732       break;
1733     }
1734 
1735   return rect;
1736 }
1737 
1738 #define TOP_RESIZE_HEIGHT 4
1739 #define CORNER_SIZE_MULT 2
1740 static MetaFrameControl
get_control(MetaUIFrame * frame,int root_x,int root_y)1741 get_control (MetaUIFrame *frame, int root_x, int root_y)
1742 {
1743   MetaFrameGeometry fgeom;
1744   MetaFrameFlags flags;
1745   MetaFrameType type;
1746   gboolean has_vert, has_horiz;
1747   gboolean has_north_resize;
1748   cairo_rectangle_int_t client;
1749   int x, y;
1750   int win_x, win_y;
1751 
1752   if (meta_window_is_fullscreen (frame->meta_window))
1753     return META_FRAME_CONTROL_CLIENT_AREA;
1754 
1755   gdk_window_get_position (frame->window, &win_x, &win_y);
1756   x = root_x - win_x;
1757   y = root_y - win_y;
1758 
1759   meta_window_get_client_area_rect (frame->meta_window, &client);
1760   if (META_POINT_IN_RECT (x, y, client))
1761     return META_FRAME_CONTROL_CLIENT_AREA;
1762 
1763   meta_ui_frame_calc_geometry (frame, &fgeom);
1764 
1765   if (META_POINT_IN_RECT (x, y, fgeom.close_rect.clickable))
1766     return META_FRAME_CONTROL_DELETE;
1767 
1768   if (META_POINT_IN_RECT (x, y, fgeom.min_rect.clickable))
1769     return META_FRAME_CONTROL_MINIMIZE;
1770 
1771   if (META_POINT_IN_RECT (x, y, fgeom.menu_rect.clickable))
1772     return META_FRAME_CONTROL_MENU;
1773 
1774   flags = meta_frame_get_flags (frame->meta_window->frame);
1775   type = meta_window_get_frame_type (frame->meta_window);
1776 
1777   has_north_resize = (type != META_FRAME_TYPE_ATTACHED);
1778   has_vert = (flags & META_FRAME_ALLOWS_VERTICAL_RESIZE) != 0;
1779   has_horiz = (flags & META_FRAME_ALLOWS_HORIZONTAL_RESIZE) != 0;
1780 
1781   if (flags & META_FRAME_TILED_LEFT || flags & META_FRAME_TILED_RIGHT)
1782     has_vert = has_horiz = FALSE;
1783 
1784   if (META_POINT_IN_RECT (x, y, fgeom.title_rect))
1785     {
1786       if (has_vert && y <= TOP_RESIZE_HEIGHT && has_north_resize)
1787         return META_FRAME_CONTROL_RESIZE_N;
1788       else
1789         return META_FRAME_CONTROL_TITLE;
1790     }
1791 
1792   if (META_POINT_IN_RECT (x, y, fgeom.max_rect.clickable))
1793     {
1794       if (flags & META_FRAME_MAXIMIZED)
1795         return META_FRAME_CONTROL_UNMAXIMIZE;
1796       else
1797         return META_FRAME_CONTROL_MAXIMIZE;
1798     }
1799 
1800   /* South resize always has priority over north resize,
1801    * in case of overlap.
1802    */
1803 
1804   if (y >= (fgeom.height - fgeom.borders.total.bottom * CORNER_SIZE_MULT) &&
1805       x >= (fgeom.width - fgeom.borders.total.right * CORNER_SIZE_MULT))
1806     {
1807       if (has_vert && has_horiz)
1808         return META_FRAME_CONTROL_RESIZE_SE;
1809       else if (has_vert)
1810         return META_FRAME_CONTROL_RESIZE_S;
1811       else if (has_horiz)
1812         return META_FRAME_CONTROL_RESIZE_E;
1813     }
1814   else if (y >= (fgeom.height - fgeom.borders.total.bottom * CORNER_SIZE_MULT) &&
1815            x <= fgeom.borders.total.left * CORNER_SIZE_MULT)
1816     {
1817       if (has_vert && has_horiz)
1818         return META_FRAME_CONTROL_RESIZE_SW;
1819       else if (has_vert)
1820         return META_FRAME_CONTROL_RESIZE_S;
1821       else if (has_horiz)
1822         return META_FRAME_CONTROL_RESIZE_W;
1823     }
1824   else if (y < (fgeom.borders.invisible.top * CORNER_SIZE_MULT) &&
1825            x <= (fgeom.borders.total.left * CORNER_SIZE_MULT) && has_north_resize)
1826     {
1827       if (has_vert && has_horiz)
1828         return META_FRAME_CONTROL_RESIZE_NW;
1829       else if (has_vert)
1830         return META_FRAME_CONTROL_RESIZE_N;
1831       else if (has_horiz)
1832         return META_FRAME_CONTROL_RESIZE_W;
1833     }
1834   else if (y < (fgeom.borders.invisible.top * CORNER_SIZE_MULT) &&
1835            x >= (fgeom.width - fgeom.borders.total.right * CORNER_SIZE_MULT) && has_north_resize)
1836     {
1837       if (has_vert && has_horiz)
1838         return META_FRAME_CONTROL_RESIZE_NE;
1839       else if (has_vert)
1840         return META_FRAME_CONTROL_RESIZE_N;
1841       else if (has_horiz)
1842         return META_FRAME_CONTROL_RESIZE_E;
1843     }
1844   else if (y < (fgeom.borders.invisible.top + TOP_RESIZE_HEIGHT))
1845     {
1846       if (has_vert && has_north_resize)
1847         return META_FRAME_CONTROL_RESIZE_N;
1848     }
1849   else if (y >= (fgeom.height - fgeom.borders.total.bottom))
1850     {
1851       if (has_vert)
1852         return META_FRAME_CONTROL_RESIZE_S;
1853     }
1854   else if (x <= fgeom.borders.total.left)
1855     {
1856       if (has_horiz || flags & META_FRAME_TILED_RIGHT)
1857         return META_FRAME_CONTROL_RESIZE_W;
1858     }
1859   else if (x >= (fgeom.width - fgeom.borders.total.right))
1860     {
1861       if (has_horiz || flags & META_FRAME_TILED_LEFT)
1862         return META_FRAME_CONTROL_RESIZE_E;
1863     }
1864 
1865   if (y >= fgeom.borders.total.top)
1866     return META_FRAME_CONTROL_NONE;
1867   else
1868     return META_FRAME_CONTROL_TITLE;
1869 }
1870