1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2
3 /**
4 * SECTION:meta-window-actor
5 * @title: MetaWindowActor
6 * @short_description: An actor representing a top-level window in the scene graph
7 */
8
9 #include <config.h>
10
11 #include <math.h>
12
13 #include <X11/extensions/shape.h>
14 #include <X11/extensions/Xcomposite.h>
15 #include <X11/extensions/Xdamage.h>
16 #include <X11/extensions/Xrender.h>
17
18 #include <clutter/x11/clutter-x11.h>
19 #include <cogl/winsys/cogl-texture-pixmap-x11.h>
20 #include <gdk/gdk.h> /* for gdk_rectangle_union() */
21
22 #include <meta/display.h>
23 #include <meta/errors.h>
24 #include "frame.h"
25 #include <meta/window.h>
26 #include <meta/meta-shaped-texture.h>
27 #include "xprops.h"
28
29 #include "compositor-private.h"
30 #include "meta-shaped-texture-private.h"
31 #include "meta-shadow-factory-private.h"
32 #include "meta-window-actor-private.h"
33
34 enum {
35 POSITION_CHANGED,
36 SIZE_CHANGED,
37 LAST_SIGNAL
38 };
39
40 static guint signals[LAST_SIGNAL] = {0};
41
42
43 struct _MetaWindowActorPrivate
44 {
45 MetaWindow *window;
46 Window xwindow;
47 MetaScreen *screen;
48
49 ClutterActor *actor;
50
51 /* MetaShadowFactory only caches shadows that are actually in use;
52 * to avoid unnecessary recomputation we do two things: 1) we store
53 * both a focused and unfocused shadow for the window. If the window
54 * doesn't have different focused and unfocused shadow parameters,
55 * these will be the same. 2) when the shadow potentially changes we
56 * don't immediately unreference the old shadow, we just flag it as
57 * dirty and recompute it when we next need it (recompute_focused_shadow,
58 * recompute_unfocused_shadow.) Because of our extraction of
59 * size-invariant window shape, we'll often find that the new shadow
60 * is the same as the old shadow.
61 */
62 MetaShadow *focused_shadow;
63 MetaShadow *unfocused_shadow;
64
65 Pixmap back_pixmap;
66
67 Damage damage;
68
69 guint8 opacity;
70
71 /* If the window is shaped, a region that matches the shape */
72 cairo_region_t *shape_region;
73 /* The opaque region, from _NET_WM_OPAQUE_REGION, intersected with
74 * the shape region. */
75 cairo_region_t *opaque_region;
76 /* The region we should clip to when painting the shadow */
77 cairo_region_t *shadow_clip;
78
79 /* The region that is visible, used to optimize out redraws */
80 cairo_region_t *unobscured_region;
81
82 /* Extracted size-invariant shape used for shadows */
83 MetaWindowShape *shadow_shape;
84
85 gint last_width;
86 gint last_height;
87 gint last_x;
88 gint last_y;
89
90 gint freeze_count;
91
92 char * shadow_class;
93
94 /*
95 * These need to be counters rather than flags, since more plugins
96 * can implement same effect; the practicality of stacking effects
97 * might be dubious, but we have to at least handle it correctly.
98 */
99 gint minimize_in_progress;
100 gint maximize_in_progress;
101 gint unmaximize_in_progress;
102 gint tile_in_progress;
103 gint map_in_progress;
104 gint destroy_in_progress;
105
106 /* List of FrameData for recent frames */
107 GList *frames;
108
109 guint visible : 1;
110 guint argb32 : 1;
111 guint disposed : 1;
112 guint redecorating : 1;
113
114 guint needs_damage_all : 1;
115 guint received_damage : 1;
116 guint repaint_scheduled : 1;
117
118 /* If set, the client needs to be sent a _NET_WM_FRAME_DRAWN
119 * client message using the most recent frame in ->frames */
120 guint send_frame_messages_timer;
121 gint64 frame_drawn_time;
122 guint needs_frame_drawn : 1;
123
124 guint size_changed_id;
125 guint opacity_changed_id;
126
127 guint needs_pixmap : 1;
128 guint needs_reshape : 1;
129 guint recompute_focused_shadow : 1;
130 guint recompute_unfocused_shadow : 1;
131 guint size_changed : 1;
132 guint position_changed : 1;
133 guint updates_frozen : 1;
134
135 guint needs_destroy : 1;
136
137 guint no_shadow : 1;
138
139 guint unredirected : 1;
140
141 /* This is used to detect fullscreen windows that need to be unredirected */
142 guint full_damage_frames_count;
143 guint does_full_damage : 1;
144
145 guint has_desat_effect : 1;
146
147 guint reshapes;
148 guint should_have_shadow : 1;
149 };
150
151 typedef struct _FrameData FrameData;
152
153 struct _FrameData
154 {
155 int64_t frame_counter;
156 guint64 sync_request_serial;
157 gint64 frame_drawn_time;
158 };
159
160 enum
161 {
162 PROP_META_WINDOW = 1,
163 PROP_META_SCREEN,
164 PROP_X_WINDOW,
165 PROP_X_WINDOW_ATTRIBUTES,
166 PROP_NO_SHADOW,
167 PROP_SHADOW_CLASS
168 };
169
170 #define DEFAULT_SHADOW_RADIUS 12
171 #define DEFAULT_SHADOW_X_OFFSET 0
172 #define DEFAULT_SHADOW_Y_OFFSET 8
173
174 static void meta_window_actor_dispose (GObject *object);
175 static void meta_window_actor_finalize (GObject *object);
176 static void meta_window_actor_constructed (GObject *object);
177 static void meta_window_actor_set_property (GObject *object,
178 guint prop_id,
179 const GValue *value,
180 GParamSpec *pspec);
181 static void meta_window_actor_get_property (GObject *object,
182 guint prop_id,
183 GValue *value,
184 GParamSpec *pspec);
185
186 static void meta_window_actor_pick (ClutterActor *actor,
187 const ClutterColor *color);
188 static void meta_window_actor_paint (ClutterActor *actor);
189
190 static gboolean meta_window_actor_get_paint_volume (ClutterActor *actor,
191 ClutterPaintVolume *volume);
192
193
194 static void meta_window_actor_detach (MetaWindowActor *self);
195 static gboolean meta_window_actor_has_shadow (MetaWindowActor *self);
196
197 static void meta_window_actor_handle_updates (MetaWindowActor *self);
198
199 static void check_needs_reshape (MetaWindowActor *self);
200
201 static void do_send_frame_drawn (MetaWindowActor *self, FrameData *frame);
202 static void do_send_frame_timings (MetaWindowActor *self,
203 FrameData *frame,
204 gint refresh_interval,
205 gint64 presentation_time);
206
207 G_DEFINE_TYPE (MetaWindowActor, meta_window_actor, CLUTTER_TYPE_ACTOR);
208
209 static void
frame_data_free(FrameData * frame)210 frame_data_free (FrameData *frame)
211 {
212 g_slice_free (FrameData, frame);
213 }
214
215 static void
meta_window_actor_class_init(MetaWindowActorClass * klass)216 meta_window_actor_class_init (MetaWindowActorClass *klass)
217 {
218 GObjectClass *object_class = G_OBJECT_CLASS (klass);
219 ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
220 GParamSpec *pspec;
221
222 g_type_class_add_private (klass, sizeof (MetaWindowActorPrivate));
223
224 object_class->dispose = meta_window_actor_dispose;
225 object_class->finalize = meta_window_actor_finalize;
226 object_class->set_property = meta_window_actor_set_property;
227 object_class->get_property = meta_window_actor_get_property;
228 object_class->constructed = meta_window_actor_constructed;
229
230 actor_class->pick = meta_window_actor_pick;
231 actor_class->paint = meta_window_actor_paint;
232 actor_class->get_paint_volume = meta_window_actor_get_paint_volume;
233
234 pspec = g_param_spec_object ("meta-window",
235 "MetaWindow",
236 "The displayed MetaWindow",
237 META_TYPE_WINDOW,
238 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
239
240 g_object_class_install_property (object_class,
241 PROP_META_WINDOW,
242 pspec);
243
244 pspec = g_param_spec_pointer ("meta-screen",
245 "MetaScreen",
246 "MetaScreen",
247 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
248
249 g_object_class_install_property (object_class,
250 PROP_META_SCREEN,
251 pspec);
252
253 pspec = g_param_spec_ulong ("x-window",
254 "Window",
255 "Window",
256 0,
257 G_MAXULONG,
258 0,
259 G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
260
261 g_object_class_install_property (object_class,
262 PROP_X_WINDOW,
263 pspec);
264
265 pspec = g_param_spec_boolean ("no-shadow",
266 "No shadow",
267 "Do not add shaddow to this window",
268 FALSE,
269 G_PARAM_READWRITE);
270
271 g_object_class_install_property (object_class,
272 PROP_NO_SHADOW,
273 pspec);
274
275 pspec = g_param_spec_string ("shadow-class",
276 "Name of the shadow class for this window.",
277 "NULL means to use the default shadow class for this window type",
278 NULL,
279 G_PARAM_READWRITE);
280
281 g_object_class_install_property (object_class,
282 PROP_SHADOW_CLASS,
283 pspec);
284
285 signals[POSITION_CHANGED] =
286 g_signal_new ("position-changed",
287 G_TYPE_FROM_CLASS (klass),
288 G_SIGNAL_RUN_LAST,
289 0, NULL, NULL, NULL,
290 G_TYPE_NONE, 0);
291 signals[SIZE_CHANGED] =
292 g_signal_new ("size-changed",
293 G_TYPE_FROM_CLASS (klass),
294 G_SIGNAL_RUN_LAST,
295 0, NULL, NULL, NULL,
296 G_TYPE_NONE, 0);
297 }
298
299 static void
meta_window_actor_init(MetaWindowActor * self)300 meta_window_actor_init (MetaWindowActor *self)
301 {
302 MetaWindowActorPrivate *priv;
303
304 priv = self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
305 META_TYPE_WINDOW_ACTOR,
306 MetaWindowActorPrivate);
307 priv->opacity = 0xff;
308 priv->shadow_class = NULL;
309 priv->has_desat_effect = FALSE;
310 priv->reshapes = 0;
311 priv->should_have_shadow = FALSE;
312 }
313
314 static void
meta_window_actor_reset_mask_texture(MetaWindowActor * self,cairo_region_t * shape_region,gboolean force)315 meta_window_actor_reset_mask_texture (MetaWindowActor *self,
316 cairo_region_t *shape_region,
317 gboolean force)
318 {
319 MetaShapedTexture *stex = META_SHAPED_TEXTURE (self->priv->actor);
320 if (force)
321 meta_shaped_texture_dirty_mask (stex);
322 meta_shaped_texture_ensure_mask (stex, shape_region, self->priv->window->frame != NULL);
323 }
324
325 static void
maybe_desaturate_window(ClutterActor * actor)326 maybe_desaturate_window (ClutterActor *actor)
327 {
328 MetaWindowActor *window = META_WINDOW_ACTOR (actor);
329 MetaWindowActorPrivate *priv = window->priv;
330
331 if (!priv->should_have_shadow && !priv->has_desat_effect)
332 return;
333
334 guint8 opacity = clutter_actor_get_opacity (actor);
335
336 if (opacity < 255)
337 {
338 if (priv->has_desat_effect)
339 {
340 return;
341 }
342 else
343 {
344 ClutterEffect *effect = clutter_desaturate_effect_new (0.0);
345 clutter_actor_add_effect_with_name (actor, "desaturate-for-transparency", effect);
346 priv->has_desat_effect = TRUE;
347 }
348 }
349 else
350 {
351 /* This is will tend to get called fairly often - opening new windows, various
352 events on the window, like minimizing... but it's inexpensive - if the ClutterActor
353 priv->effects is NULL, it simply returns. By default cinnamon and muffin add no
354 other effects except the special case of dimmed windows (attached modal dialogs), which
355 isn't a frequent occurrence. */
356
357 clutter_actor_remove_effect_by_name (actor, "desaturate-for-transparency");
358 priv->has_desat_effect = FALSE;
359 }
360 }
361
362 static void
window_decorated_notify(MetaWindow * mw,GParamSpec * arg1,gpointer data)363 window_decorated_notify (MetaWindow *mw,
364 GParamSpec *arg1,
365 gpointer data)
366 {
367 MetaWindowActor *self = META_WINDOW_ACTOR (data);
368 MetaWindowActorPrivate *priv = self->priv;
369 MetaFrame *frame = meta_window_get_frame (mw);
370 MetaScreen *screen = priv->screen;
371 MetaDisplay *display = meta_screen_get_display (screen);
372 Display *xdisplay = meta_display_get_xdisplay (display);
373 Window new_xwindow;
374
375 /*
376 * Basically, we have to reconstruct the the internals of this object
377 * from scratch, as everything has changed.
378 */
379 priv->redecorating = TRUE;
380
381 if (frame)
382 new_xwindow = meta_frame_get_xwindow (frame);
383 else
384 new_xwindow = meta_window_get_xwindow (mw);
385
386 meta_window_actor_detach (self);
387
388 /*
389 * First of all, clean up any resources we are currently using and will
390 * be replacing.
391 */
392 if (priv->damage != None)
393 {
394 meta_error_trap_push (display);
395 XDamageDestroy (xdisplay, priv->damage);
396 meta_error_trap_pop (display);
397 priv->damage = None;
398 }
399
400 priv->xwindow = new_xwindow;
401
402 /*
403 * Recreate the contents.
404 */
405 meta_window_actor_constructed (G_OBJECT (self));
406 }
407
408 static void
window_appears_focused_notify(MetaWindow * mw,GParamSpec * arg1,gpointer data)409 window_appears_focused_notify (MetaWindow *mw,
410 GParamSpec *arg1,
411 gpointer data)
412 {
413 clutter_actor_queue_redraw (CLUTTER_ACTOR (data));
414 }
415
416 static void
clutter_actor_opacity_notify(ClutterActor * actor,GParamSpec * arg1m,gpointer data)417 clutter_actor_opacity_notify (ClutterActor *actor,
418 GParamSpec *arg1m,
419 gpointer data)
420 {
421 maybe_desaturate_window (actor);
422 }
423
424 static void
texture_size_changed(MetaWindow * mw,gpointer data)425 texture_size_changed (MetaWindow *mw,
426 gpointer data)
427 {
428 MetaWindowActor *self = META_WINDOW_ACTOR (data);
429
430 g_signal_emit (self, signals[SIZE_CHANGED], 0); // Compatibility
431 }
432
433 static void
window_position_changed(MetaWindow * mw,gpointer data)434 window_position_changed (MetaWindow *mw,
435 gpointer data)
436 {
437 MetaWindowActor *self = META_WINDOW_ACTOR (data);
438
439 g_signal_emit (self, signals[POSITION_CHANGED], 0); // Compatibility
440 }
441
442 static void
meta_window_actor_constructed(GObject * object)443 meta_window_actor_constructed (GObject *object)
444 {
445 MetaWindowActor *self = META_WINDOW_ACTOR (object);
446 MetaWindowActorPrivate *priv = self->priv;
447 MetaScreen *screen = priv->screen;
448 MetaDisplay *display = meta_screen_get_display (screen);
449 Window xwindow = priv->xwindow;
450 MetaWindow *window = priv->window;
451 Display *xdisplay = meta_display_get_xdisplay (display);
452 XRenderPictFormat *format;
453
454 priv->damage = XDamageCreate (xdisplay, xwindow,
455 XDamageReportBoundingBox);
456
457 format = XRenderFindVisualFormat (xdisplay, window->xvisual);
458
459 if (format && format->type == PictTypeDirect && format->direct.alphaMask)
460 priv->argb32 = TRUE;
461
462 if (!priv->actor)
463 {
464 priv->actor = meta_shaped_texture_new ();
465
466 priv->size_changed_id = g_signal_connect (priv->actor, "size-changed",
467 G_CALLBACK (texture_size_changed), self);
468 clutter_actor_add_child (CLUTTER_ACTOR (self), priv->actor);
469
470 /*
471 * Since we are holding a pointer to this actor independently of the
472 * ClutterContainer internals, and provide a public API to access it,
473 * add a reference here, so that if someone is messing about with us
474 * via the container interface, we do not end up with a dangling pointer.
475 * We will release it in dispose().
476 */
477 g_object_ref (priv->actor);
478 priv->opacity_changed_id = g_signal_connect (self, "notify::opacity",
479 G_CALLBACK (clutter_actor_opacity_notify), NULL);
480
481 /* Fix for the case when clients try to re-map their windows after re-decorating while
482 effects are enabled. For reasons currently unknown, the re-shape doesn't happen when
483 #meta_plugin_map_completed is called after a delay. #window_decorated_notify is not
484 always called on re-decoration, and when it is, its only called before the actor is
485 disposed in this case - we can't track after that. */
486 if (meta_prefs_get_desktop_effects () &&
487 window->frame != NULL &&
488 window->decorated &&
489 !window->pending_compositor_effect &&
490 !window->unmaps_pending)
491 priv->needs_reshape = TRUE;
492 }
493 else
494 {
495 /*
496 * This is the case where existing window is gaining/loosing frame.
497 * Just ensure the actor is top most (i.e., above shadow).
498 */
499 g_signal_handler_disconnect (priv->actor, priv->size_changed_id);
500 g_signal_handler_disconnect (self, priv->opacity_changed_id);
501 clutter_actor_set_child_above_sibling (CLUTTER_ACTOR (self), priv->actor, NULL);
502 }
503
504 meta_window_actor_update_opacity (self);
505 maybe_desaturate_window (CLUTTER_ACTOR (self));
506
507 priv->shape_region = cairo_region_create();
508 }
509
510 static void
meta_window_actor_dispose(GObject * object)511 meta_window_actor_dispose (GObject *object)
512 {
513 MetaWindowActor *self = META_WINDOW_ACTOR (object);
514 MetaWindowActorPrivate *priv = self->priv;
515 MetaScreen *screen;
516 MetaDisplay *display;
517 Display *xdisplay;
518 MetaCompositor *compositor;
519
520 if (priv->disposed)
521 return;
522
523 priv->disposed = TRUE;
524
525 if (priv->send_frame_messages_timer != 0)
526 {
527 g_source_remove (priv->send_frame_messages_timer);
528 priv->send_frame_messages_timer = 0;
529 }
530
531 screen = priv->screen;
532 display = screen->display;
533 xdisplay = display->xdisplay;
534 compositor = display->compositor;
535
536 meta_window_actor_detach (self);
537
538 g_clear_pointer (&priv->unobscured_region, cairo_region_destroy);
539 g_clear_pointer (&priv->shape_region, cairo_region_destroy);
540 g_clear_pointer (&priv->opaque_region, cairo_region_destroy);
541 g_clear_pointer (&priv->shadow_clip, cairo_region_destroy);
542
543 g_clear_pointer (&priv->shadow_class, free);
544 g_clear_pointer (&priv->focused_shadow, meta_shadow_unref);
545 g_clear_pointer (&priv->unfocused_shadow, meta_shadow_unref);
546 g_clear_pointer (&priv->shadow_shape, meta_window_shape_unref);
547
548 if (priv->damage != None)
549 {
550 meta_error_trap_push (display);
551 XDamageDestroy (xdisplay, priv->damage);
552 meta_error_trap_pop (display);
553
554 priv->damage = None;
555 }
556
557 compositor->windows = g_list_remove (compositor->windows, (gconstpointer) self);
558
559 g_clear_object (&priv->window);
560
561 /*
562 * Release the extra reference we took on the actor.
563 */
564 g_clear_object (&priv->actor);
565
566 G_OBJECT_CLASS (meta_window_actor_parent_class)->dispose (object);
567 }
568
569 static void
meta_window_actor_finalize(GObject * object)570 meta_window_actor_finalize (GObject *object)
571 {
572 MetaWindowActor *self = META_WINDOW_ACTOR (object);
573 MetaWindowActorPrivate *priv = self->priv;
574 g_list_free_full (priv->frames, (GDestroyNotify) frame_data_free);
575 G_OBJECT_CLASS (meta_window_actor_parent_class)->finalize (object);
576 }
577
578 static void
meta_window_actor_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)579 meta_window_actor_set_property (GObject *object,
580 guint prop_id,
581 const GValue *value,
582 GParamSpec *pspec)
583 {
584 MetaWindowActor *self = META_WINDOW_ACTOR (object);
585 MetaWindowActorPrivate *priv = self->priv;
586
587 switch (prop_id)
588 {
589 case PROP_META_WINDOW:
590 {
591 if (priv->window)
592 g_object_unref (priv->window);
593 priv->window = g_value_dup_object (value);
594
595 g_signal_connect_object (priv->window, "notify::decorated",
596 G_CALLBACK (window_decorated_notify), self, 0);
597 g_signal_connect_object (priv->window, "notify::appears-focused",
598 G_CALLBACK (window_appears_focused_notify), self, 0);
599 g_signal_connect_object (priv->window, "position-changed",
600 G_CALLBACK (window_position_changed), self, 0);
601 }
602 break;
603 case PROP_META_SCREEN:
604 priv->screen = g_value_get_pointer (value);
605 break;
606 case PROP_X_WINDOW:
607 priv->xwindow = g_value_get_ulong (value);
608 break;
609 case PROP_NO_SHADOW:
610 {
611 gboolean newv = g_value_get_boolean (value);
612
613 if (newv == priv->no_shadow)
614 return;
615
616 priv->no_shadow = newv;
617
618 meta_window_actor_invalidate_shadow (self);
619 }
620 break;
621 case PROP_SHADOW_CLASS:
622 {
623 const char *newv = g_value_get_string (value);
624
625 if (g_strcmp0 (newv, priv->shadow_class) == 0)
626 return;
627
628 free (priv->shadow_class);
629 priv->shadow_class = g_strdup (newv);
630
631 meta_window_actor_invalidate_shadow (self);
632 }
633 break;
634 default:
635 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
636 break;
637 }
638 }
639
640 static void
meta_window_actor_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)641 meta_window_actor_get_property (GObject *object,
642 guint prop_id,
643 GValue *value,
644 GParamSpec *pspec)
645 {
646 MetaWindowActorPrivate *priv = META_WINDOW_ACTOR (object)->priv;
647
648 switch (prop_id)
649 {
650 case PROP_META_WINDOW:
651 g_value_set_object (value, priv->window);
652 break;
653 case PROP_META_SCREEN:
654 g_value_set_pointer (value, priv->screen);
655 break;
656 case PROP_X_WINDOW:
657 g_value_set_ulong (value, priv->xwindow);
658 break;
659 case PROP_NO_SHADOW:
660 g_value_set_boolean (value, priv->no_shadow);
661 break;
662 case PROP_SHADOW_CLASS:
663 g_value_set_string (value, priv->shadow_class);
664 break;
665 default:
666 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
667 break;
668 }
669 }
670
671 static const char *
meta_window_actor_get_shadow_class(MetaWindowActor * self)672 meta_window_actor_get_shadow_class (MetaWindowActor *self)
673 {
674 MetaWindowActorPrivate *priv = self->priv;
675
676 if (priv->shadow_class != NULL)
677 return priv->shadow_class;
678 else
679 {
680 MetaWindowType window_type = meta_window_get_window_type (priv->window);
681
682 switch (window_type)
683 {
684 case META_WINDOW_DROPDOWN_MENU:
685 return "dropdown-menu";
686 case META_WINDOW_POPUP_MENU:
687 return "popup-menu";
688 default:
689 {
690 MetaFrameType frame_type = meta_window_get_frame_type (priv->window);
691 return meta_frame_type_to_string (frame_type);
692 }
693 }
694 }
695 }
696
697 static void
meta_window_actor_get_shadow_params(MetaWindowActor * self,gboolean appears_focused,MetaShadowParams * params)698 meta_window_actor_get_shadow_params (MetaWindowActor *self,
699 gboolean appears_focused,
700 MetaShadowParams *params)
701 {
702 const char *shadow_class = meta_window_actor_get_shadow_class (self);
703
704 meta_shadow_factory_get_params (meta_shadow_factory_get_default (),
705 shadow_class, appears_focused,
706 params);
707 }
708
709 LOCAL_SYMBOL void
meta_window_actor_get_shape_bounds(MetaWindowActor * self,cairo_rectangle_int_t * bounds)710 meta_window_actor_get_shape_bounds (MetaWindowActor *self,
711 cairo_rectangle_int_t *bounds)
712 {
713 MetaWindowActorPrivate *priv = self->priv;
714
715 /* We need to be defensive here because there are corner cases
716 * where getting the shape fails on a window being destroyed
717 * and similar.
718 */
719 if (priv->shape_region)
720 cairo_region_get_extents (priv->shape_region, bounds);
721 else
722 bounds->x = bounds->y = bounds->width = bounds->height = 0;
723 }
724
725 static void
meta_window_actor_get_shadow_bounds(MetaWindowActor * self,gboolean appears_focused,cairo_rectangle_int_t * bounds)726 meta_window_actor_get_shadow_bounds (MetaWindowActor *self,
727 gboolean appears_focused,
728 cairo_rectangle_int_t *bounds)
729 {
730 MetaWindowActorPrivate *priv = self->priv;
731 MetaShadow *shadow = appears_focused ? priv->focused_shadow : priv->unfocused_shadow;
732 cairo_rectangle_int_t shape_bounds;
733 MetaShadowParams params;
734
735 meta_window_actor_get_shape_bounds (self, &shape_bounds);
736 meta_window_actor_get_shadow_params (self, appears_focused, ¶ms);
737
738 meta_shadow_get_bounds (shadow,
739 params.x_offset + shape_bounds.x,
740 params.y_offset + shape_bounds.y,
741 shape_bounds.width,
742 shape_bounds.height,
743 bounds);
744 }
745
746 /* If we have an ARGB32 window that we decorate with a frame, it's
747 * probably something like a translucent terminal - something where
748 * the alpha channel represents transparency rather than a shape. We
749 * don't want to show the shadow through the translucent areas since
750 * the shadow is wrong for translucent windows (it should be
751 * translucent itself and colored), and not only that, will /look/
752 * horribly wrong - a misplaced big black blob. As a hack, what we
753 * want to do is just draw the shadow as normal outside the frame, and
754 * inside the frame draw no shadow. This is also not even close to
755 * the right result, but looks OK. We also apply this approach to
756 * windows set to be partially translucent with _NET_WM_WINDOW_OPACITY.
757 */
758 static gboolean
clip_shadow_under_window(MetaWindowActor * self)759 clip_shadow_under_window (MetaWindowActor *self)
760 {
761 MetaWindowActorPrivate *priv = self->priv;
762
763 return (priv->argb32 || priv->opacity != 0xff) && priv->window->frame;
764 }
765
766 static void
assign_frame_counter_to_frames(MetaWindowActor * self)767 assign_frame_counter_to_frames (MetaWindowActor *self)
768 {
769 MetaWindowActorPrivate *priv = self->priv;
770 ClutterStage *stage = priv->window->display->compositor->stage;
771 GList *l;
772
773 /* If the window is obscured, then we're expecting to deal with sending
774 * frame messages in a timeout, rather than in this paint cycle.
775 */
776 if (priv->send_frame_messages_timer != 0)
777 return;
778
779 for (l = priv->frames; l; l = l->next)
780 {
781 FrameData *frame = l->data;
782
783 if (frame->frame_counter == -1)
784 frame->frame_counter = clutter_stage_get_frame_counter (stage);
785 }
786 }
787
788 static void
meta_window_actor_pick(ClutterActor * actor,const ClutterColor * color)789 meta_window_actor_pick (ClutterActor *actor,
790 const ClutterColor *color)
791 {
792 if (!clutter_actor_should_pick_paint (actor))
793 return;
794
795 MetaWindowActor *self = (MetaWindowActor *) actor;
796 MetaWindowActorPrivate *priv = self->priv;
797 ClutterActorIter iter;
798 ClutterActor *child;
799
800 /* If there is no region then use the regular pick */
801 if (priv->shape_region == NULL)
802 CLUTTER_ACTOR_CLASS (meta_window_actor_parent_class)->pick (actor, color);
803 else
804 {
805 int n_rects;
806 float *rectangles;
807 int i;
808 CoglPipeline *pipeline;
809 CoglContext *ctx;
810 CoglFramebuffer *fb;
811 CoglColor cogl_color;
812
813 n_rects = cairo_region_num_rectangles (priv->shape_region);
814 rectangles = g_alloca (sizeof (float) * 4 * n_rects);
815
816 for (i = 0; i < n_rects; i++)
817 {
818 cairo_rectangle_int_t rect;
819 int pos = i * 4;
820
821 cairo_region_get_rectangle (priv->shape_region, i, &rect);
822
823 rectangles[pos + 0] = rect.x;
824 rectangles[pos + 1] = rect.y;
825 rectangles[pos + 2] = rect.x + rect.width;
826 rectangles[pos + 3] = rect.y + rect.height;
827 }
828
829 ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
830 fb = cogl_get_draw_framebuffer ();
831
832 cogl_color_init_from_4ub (&cogl_color, color->red, color->green, color->blue, color->alpha);
833
834 pipeline = cogl_pipeline_new (ctx);
835 cogl_pipeline_set_color (pipeline, &cogl_color);
836 cogl_framebuffer_draw_rectangles (fb, pipeline, rectangles, n_rects);
837 cogl_object_unref (pipeline);
838 }
839
840 clutter_actor_iter_init (&iter, actor);
841
842 while (clutter_actor_iter_next (&iter, &child))
843 clutter_actor_paint (child);
844 }
845
846 static void
meta_window_actor_paint(ClutterActor * actor)847 meta_window_actor_paint (ClutterActor *actor)
848 {
849 MetaWindowActor *self = META_WINDOW_ACTOR (actor);
850 MetaWindowActorPrivate *priv = self->priv;
851 CoglFramebuffer *framebuffer = NULL;
852 gboolean appears_focused = meta_window_appears_focused (priv->window);
853 MetaShadow *shadow = appears_focused ? priv->focused_shadow : priv->unfocused_shadow;
854
855 if (!priv->window->display->shadows_enabled) {
856 shadow = NULL;
857 }
858
859 /* This window got damage when obscured; we set up a timer
860 * to send frame completion events, but since we're drawing
861 * the window now (for some other reason) cancel the timer
862 * and send the completion events normally */
863 if (priv->send_frame_messages_timer != 0)
864 {
865 g_source_remove (priv->send_frame_messages_timer);
866 priv->send_frame_messages_timer = 0;
867
868 assign_frame_counter_to_frames (self);
869 }
870
871 if (shadow != NULL)
872 {
873 MetaShadowParams params;
874 cairo_rectangle_int_t shape_bounds;
875 cairo_region_t *clip = priv->shadow_clip;
876
877 meta_window_actor_get_shape_bounds (self, &shape_bounds);
878 meta_window_actor_get_shadow_params (self, appears_focused, ¶ms);
879
880 /* The frame bounds are already subtracted from priv->shadow_clip
881 * if that exists.
882 */
883 if (!clip && clip_shadow_under_window (self))
884 {
885 cairo_rectangle_int_t bounds;
886
887 meta_window_actor_get_shadow_bounds (self, appears_focused, &bounds);
888 clip = cairo_region_create_rectangle (&bounds);
889
890 cairo_region_subtract (clip, meta_window_get_frame_bounds (priv->window));
891 }
892
893 framebuffer = cogl_get_draw_framebuffer ();
894
895 meta_shadow_paint (shadow,
896 framebuffer,
897 params.x_offset + shape_bounds.x,
898 params.y_offset + shape_bounds.y,
899 shape_bounds.width,
900 shape_bounds.height,
901 (clutter_actor_get_paint_opacity (actor) * params.opacity * priv->opacity) / (255 * 255),
902 clip,
903 clip_shadow_under_window (self)); /* clip_strictly - not just as an optimization */
904
905 if (clip && clip != priv->shadow_clip)
906 cairo_region_destroy (clip);
907 }
908
909 CLUTTER_ACTOR_CLASS (meta_window_actor_parent_class)->paint (actor);
910 }
911
912 static gboolean
meta_window_actor_get_paint_volume(ClutterActor * actor,ClutterPaintVolume * volume)913 meta_window_actor_get_paint_volume (ClutterActor *actor,
914 ClutterPaintVolume *volume)
915 {
916 MetaWindowActor *self = META_WINDOW_ACTOR (actor);
917 MetaWindowActorPrivate *priv = self->priv;
918 gboolean appears_focused = meta_window_appears_focused (priv->window);
919
920 /* The paint volume is computed before paint functions are called
921 * so our bounds might not be updated yet. Force an update. */
922 meta_window_actor_handle_updates (self);
923
924 if (appears_focused ? priv->focused_shadow : priv->unfocused_shadow)
925 {
926 cairo_rectangle_int_t shadow_bounds;
927 ClutterActorBox shadow_box;
928
929 /* We could compute an full clip region as we do for the window
930 * texture, but the shadow is relatively cheap to draw, and
931 * a little more complex to clip, so we just catch the case where
932 * the shadow is completely obscured and doesn't need to be drawn
933 * at all.
934 */
935
936 meta_window_actor_get_shadow_bounds (self, appears_focused, &shadow_bounds);
937 shadow_box.x1 = shadow_bounds.x;
938 shadow_box.x2 = shadow_bounds.x + shadow_bounds.width;
939 shadow_box.y1 = shadow_bounds.y;
940 shadow_box.y2 = shadow_bounds.y + shadow_bounds.height;
941
942 clutter_paint_volume_union_box (volume, &shadow_box);
943 }
944
945 if (priv->actor)
946 {
947 const ClutterPaintVolume *child_volume;
948
949 child_volume = clutter_actor_get_transformed_paint_volume (CLUTTER_ACTOR (priv->actor), actor);
950 if (!child_volume)
951 return FALSE;
952
953 clutter_paint_volume_union (volume, child_volume);
954 }
955
956 return TRUE;
957 }
958
959 static gboolean
meta_window_actor_has_shadow(MetaWindowActor * self)960 meta_window_actor_has_shadow (MetaWindowActor *self)
961 {
962 MetaWindowActorPrivate *priv = self->priv;
963 MetaWindowType window_type = meta_window_get_window_type (priv->window);
964
965 if (priv->no_shadow)
966 return FALSE;
967
968 /* Leaving out shadows for maximized and fullscreen windows is an effeciency
969 * win and also prevents the unsightly effect of the shadow of maximized
970 * window appearing on an adjacent window */
971 if ((meta_window_get_maximized (priv->window) == (META_MAXIMIZE_HORIZONTAL | META_MAXIMIZE_VERTICAL)) ||
972 meta_window_is_fullscreen (priv->window))
973 return FALSE;
974
975 /* Don't shadow tiled windows of any type */
976
977 if (meta_window_get_tile_type (priv->window) != META_WINDOW_TILE_TYPE_NONE)
978 return FALSE;
979
980 /*
981 * Always put a shadow around windows with a frame - This should override
982 * the restriction about not putting a shadow around ARGB windows.
983 */
984 if (priv->window)
985 {
986 if (meta_window_get_frame (priv->window))
987 return TRUE;
988 }
989
990 /*
991 * Do not add shadows to ARGB windows; eventually we should generate a
992 * shadow from the input shape for such windows.
993 */
994 if (priv->argb32 || priv->opacity != 0xff)
995 return FALSE;
996 /*
997 * Add shadows to override redirect windows (e.g., Gtk menus).
998 */
999 if (priv->window->override_redirect)
1000 return TRUE;
1001
1002 /*
1003 * If a window specifies that it has custom frame extents, that likely
1004 * means that it is drawing a shadow itself. Don't draw our own.
1005 */
1006 if (priv->window->has_custom_frame_extents)
1007 return FALSE;
1008
1009 /*
1010 * Don't put shadow around DND icon windows
1011 */
1012 if (window_type == META_WINDOW_DND ||
1013 window_type == META_WINDOW_DESKTOP ||
1014 window_type == META_WINDOW_DOCK)
1015 return FALSE;
1016
1017 if (window_type == META_WINDOW_MENU
1018 #if 0
1019 || window_type == META_WINDOW_DROPDOWN_MENU
1020 #endif
1021 )
1022 return TRUE;
1023
1024 if (meta_window_is_client_decorated (priv->window))
1025 {
1026 return FALSE;
1027 }
1028
1029 #if 0
1030 if (window_type == META_WINDOW_TOOLTIP)
1031 return TRUE;
1032 #endif
1033
1034 return FALSE;
1035 }
1036
1037 /**
1038 * meta_window_actor_get_x_window: (skip)
1039 *
1040 */
1041 Window
meta_window_actor_get_x_window(MetaWindowActor * self)1042 meta_window_actor_get_x_window (MetaWindowActor *self)
1043 {
1044 if (!self)
1045 return None;
1046
1047 return self->priv->xwindow;
1048 }
1049
1050 /**
1051 * meta_window_actor_get_meta_window:
1052 *
1053 * Gets the #MetaWindow object that the the #MetaWindowActor is displaying
1054 *
1055 * Return value: (transfer none): the displayed #MetaWindow
1056 */
1057 MetaWindow *
meta_window_actor_get_meta_window(MetaWindowActor * self)1058 meta_window_actor_get_meta_window (MetaWindowActor *self)
1059 {
1060 return self->priv->window;
1061 }
1062
1063 /**
1064 * meta_window_actor_get_texture:
1065 *
1066 * Gets the ClutterActor that is used to display the contents of the window
1067 *
1068 * Return value: (transfer none): the #ClutterActor for the contents
1069 */
1070 ClutterActor *
meta_window_actor_get_texture(MetaWindowActor * self)1071 meta_window_actor_get_texture (MetaWindowActor *self)
1072 {
1073 return self->priv->actor;
1074 }
1075
1076 /**
1077 * meta_window_actor_is_destroyed:
1078 *
1079 * Gets whether the X window that the actor was displaying has been destroyed
1080 *
1081 * Return value: %TRUE when the window is destroyed, otherwise %FALSE
1082 */
1083 gboolean
meta_window_actor_is_destroyed(MetaWindowActor * self)1084 meta_window_actor_is_destroyed (MetaWindowActor *self)
1085 {
1086 return self->priv->disposed || self->priv->needs_destroy;
1087 }
1088
1089 static gboolean
send_frame_messages_timeout(gpointer data)1090 send_frame_messages_timeout (gpointer data)
1091 {
1092 MetaWindowActor *self = (MetaWindowActor *) data;
1093 MetaWindowActorPrivate *priv = self->priv;
1094 GList *l;
1095
1096 for (l = priv->frames; l;)
1097 {
1098 GList *l_next = l->next;
1099 FrameData *frame = l->data;
1100
1101 if (frame->frame_counter == -1)
1102 {
1103 do_send_frame_drawn (self, frame);
1104 do_send_frame_timings (self, frame, 0, 0);
1105
1106 priv->frames = g_list_delete_link (priv->frames, l);
1107 frame_data_free (frame);
1108 }
1109
1110 l = l_next;
1111 }
1112
1113 priv->needs_frame_drawn = FALSE;
1114 priv->send_frame_messages_timer = 0;
1115
1116 return FALSE;
1117 }
1118
1119 static void
queue_send_frame_messages_timeout(MetaWindowActor * self)1120 queue_send_frame_messages_timeout (MetaWindowActor *self)
1121 {
1122 MetaWindowActorPrivate *priv = self->priv;
1123 MetaWindow *window = meta_window_actor_get_meta_window (self);
1124 MetaDisplay *display = meta_screen_get_display (priv->screen);
1125 int64_t current_time;
1126 float refresh_rate;
1127 int interval, offset;
1128
1129 if (priv->send_frame_messages_timer != 0)
1130 return;
1131
1132 if (window->monitor)
1133 {
1134 refresh_rate = window->monitor->refresh_rate;
1135 }
1136 else
1137 {
1138 refresh_rate = 60.0f;
1139 }
1140
1141 current_time =
1142 meta_compositor_monotonic_time_to_server_time (display,
1143 g_get_monotonic_time ());
1144 interval = (int)(1000000 / refresh_rate) * 6;
1145 offset = MAX (0, priv->frame_drawn_time + interval - current_time) / 1000;
1146
1147 /* The clutter master clock source has already been added with META_PRIORITY_REDRAW,
1148 * so the timer will run *after* the clutter frame handling, if a frame is ready
1149 * to be drawn when the timer expires.
1150 */
1151 priv->send_frame_messages_timer = g_timeout_add_full (META_PRIORITY_REDRAW, offset, send_frame_messages_timeout, self, NULL);
1152 g_source_set_name_by_id (priv->send_frame_messages_timer, "[muffin] send_frame_messages_timeout");
1153 }
1154
1155 gboolean
meta_window_actor_is_override_redirect(MetaWindowActor * self)1156 meta_window_actor_is_override_redirect (MetaWindowActor *self)
1157 {
1158 return meta_window_is_override_redirect (self->priv->window);
1159 }
1160
1161 /**
1162 * meta_window_actor_get_workspace:
1163 * @self: #MetaWindowActor
1164 *
1165 * Returns the index of workspace on which this window is located; if the
1166 * window is sticky, or is not currently located on any workspace, returns -1.
1167 * This function is deprecated and should not be used in newly written code;
1168 * meta_window_get_workspace() instead.
1169 *
1170 * Return value: index of workspace on which this window is
1171 * located.
1172 */
1173 gint
meta_window_actor_get_workspace(MetaWindowActor * self)1174 meta_window_actor_get_workspace (MetaWindowActor *self)
1175 {
1176 MetaWindowActorPrivate *priv;
1177 MetaWorkspace *workspace;
1178
1179 if (!self)
1180 return -1;
1181
1182 priv = self->priv;
1183
1184 if (!priv->window || meta_window_is_on_all_workspaces (priv->window))
1185 return -1;
1186
1187 workspace = meta_window_get_workspace (priv->window);
1188
1189 if (!workspace)
1190 return -1;
1191
1192 return meta_workspace_index (workspace);
1193 }
1194
1195 gboolean
meta_window_actor_showing_on_its_workspace(MetaWindowActor * self)1196 meta_window_actor_showing_on_its_workspace (MetaWindowActor *self)
1197 {
1198 if (!self)
1199 return FALSE;
1200
1201 /* If override redirect: */
1202 if (!self->priv->window)
1203 return TRUE;
1204
1205 return meta_window_showing_on_its_workspace (self->priv->window);
1206 }
1207
1208 static void
meta_window_actor_freeze(MetaWindowActor * self)1209 meta_window_actor_freeze (MetaWindowActor *self)
1210 {
1211 self->priv->freeze_count++;
1212 }
1213
1214 static void
update_area(MetaWindowActor * self,int x,int y,int width,int height)1215 update_area (MetaWindowActor *self,
1216 int x, int y, int width, int height)
1217 {
1218 MetaWindowActorPrivate *priv = self->priv;
1219 CoglTexture *texture;
1220
1221 texture = meta_shaped_texture_get_texture (META_SHAPED_TEXTURE (priv->actor));
1222
1223 cogl_texture_pixmap_x11_update_area (COGL_TEXTURE_PIXMAP_X11 (texture),
1224 x, y, width, height);
1225 }
1226
1227 static void
meta_window_actor_damage_all(MetaWindowActor * self)1228 meta_window_actor_damage_all (MetaWindowActor *self)
1229 {
1230 MetaWindowActorPrivate *priv = self->priv;
1231 CoglTexture *texture;
1232
1233 if (!priv->needs_damage_all || !priv->window->mapped || priv->needs_pixmap)
1234 return;
1235
1236 texture = meta_shaped_texture_get_texture (META_SHAPED_TEXTURE (priv->actor));
1237
1238 priv->needs_damage_all = FALSE;
1239
1240 update_area (self, 0, 0, cogl_texture_get_width (texture), cogl_texture_get_height (texture));
1241 priv->repaint_scheduled = meta_shaped_texture_update_area (META_SHAPED_TEXTURE (priv->actor),
1242 0, 0,
1243 cogl_texture_get_width (texture),
1244 cogl_texture_get_height (texture),
1245 clutter_actor_has_mapped_clones (priv->actor) ? NULL : priv->unobscured_region);
1246 }
1247
1248 static void
meta_window_actor_thaw(MetaWindowActor * self)1249 meta_window_actor_thaw (MetaWindowActor *self)
1250 {
1251 self->priv->freeze_count--;
1252
1253 if (G_UNLIKELY (self->priv->freeze_count < 0))
1254 {
1255 g_warning ("Error in freeze/thaw accounting.");
1256 self->priv->freeze_count = 0;
1257 return;
1258 }
1259
1260 if (self->priv->freeze_count)
1261 return;
1262
1263 /* We sometimes ignore moves and resizes on frozen windows */
1264 meta_window_actor_sync_actor_geometry (self, FALSE);
1265
1266 /* We do this now since we might be going right back into the
1267 * frozen state */
1268 meta_window_actor_handle_updates (self);
1269
1270 /* Since we ignore damage events while a window is frozen for certain effects
1271 * we may need to issue an update_area() covering the whole pixmap if we
1272 * don't know what real damage has happened. */
1273 if (self->priv->needs_damage_all)
1274 meta_window_actor_damage_all (self);
1275 }
1276
1277 void
meta_window_actor_queue_frame_drawn(MetaWindowActor * self,gboolean no_delay_frame)1278 meta_window_actor_queue_frame_drawn (MetaWindowActor *self,
1279 gboolean no_delay_frame)
1280 {
1281 MetaWindowActorPrivate *priv = self->priv;
1282 FrameData *frame;
1283
1284 if (meta_window_actor_is_destroyed (self))
1285 return;
1286
1287 frame = g_slice_new0 (FrameData);
1288 frame->frame_counter = -1;
1289
1290 priv->needs_frame_drawn = TRUE;
1291
1292 #ifdef HAVE_XSYNC
1293 frame->sync_request_serial = priv->window->sync_request_serial;
1294 #else
1295 frame->sync_request_serial = 0;
1296 #endif
1297
1298 priv->frames = g_list_prepend (priv->frames, frame);
1299
1300 if (no_delay_frame)
1301 {
1302 ClutterActor *stage = priv->window->display->compositor->stage;
1303 clutter_stage_skip_sync_delay (CLUTTER_STAGE (stage));
1304 }
1305
1306 if (!priv->repaint_scheduled)
1307 {
1308 gboolean is_obscured = FALSE;
1309 /* Find out whether the window is completly obscured */
1310 if (priv->unobscured_region)
1311 {
1312 cairo_region_t *unobscured_window_region;
1313 unobscured_window_region = cairo_region_copy (priv->shape_region);
1314 cairo_region_intersect (unobscured_window_region, priv->unobscured_region);
1315 is_obscured = cairo_region_is_empty (unobscured_window_region);
1316 cairo_region_destroy (unobscured_window_region);
1317 }
1318
1319 /* A frame was marked by the client without actually doing any
1320 * damage, or while we had the window frozen (e.g. during an
1321 * interactive resize.) We need to make sure that the
1322 * pre_paint/post_paint functions get called, enabling us to
1323 * send a _NET_WM_FRAME_DRAWN. We do a 1-pixel redraw to get
1324 * consistent timing with non-empty frames.
1325 */
1326 if (is_obscured)
1327 {
1328 queue_send_frame_messages_timeout (self);
1329 }
1330 else if (priv->window->mapped && !priv->needs_pixmap)
1331 {
1332 const cairo_rectangle_int_t clip = { 0, 0, 1, 1 };
1333 clutter_actor_queue_redraw_with_clip (priv->actor, &clip);
1334 priv->repaint_scheduled = TRUE;
1335 }
1336 }
1337 }
1338
1339 LOCAL_SYMBOL gboolean
meta_window_actor_effect_in_progress(MetaWindowActor * self)1340 meta_window_actor_effect_in_progress (MetaWindowActor *self)
1341 {
1342 return (self->priv->minimize_in_progress ||
1343 self->priv->maximize_in_progress ||
1344 self->priv->unmaximize_in_progress ||
1345 self->priv->map_in_progress ||
1346 self->priv->tile_in_progress ||
1347 self->priv->destroy_in_progress);
1348 }
1349
1350 static gboolean
is_frozen(MetaWindowActor * self)1351 is_frozen (MetaWindowActor *self)
1352 {
1353 return self->priv->freeze_count ? TRUE : FALSE;
1354 }
1355
1356 static gboolean
is_freeze_thaw_effect(gulong event)1357 is_freeze_thaw_effect (gulong event)
1358 {
1359 switch (event)
1360 {
1361 case META_PLUGIN_DESTROY:
1362 case META_PLUGIN_MAXIMIZE:
1363 case META_PLUGIN_UNMAXIMIZE:
1364 case META_PLUGIN_TILE:
1365 return TRUE;
1366 break;
1367 default:
1368 return FALSE;
1369 }
1370 }
1371
1372 static gboolean
start_simple_effect(MetaWindowActor * self,gulong event)1373 start_simple_effect (MetaWindowActor *self,
1374 gulong event)
1375 {
1376 MetaWindowActorPrivate *priv = self->priv;
1377 MetaCompositor *compositor = priv->screen->display->compositor;
1378 gint *counter = NULL;
1379 gboolean use_freeze_thaw = FALSE;
1380
1381 if (!compositor->plugin_mgr)
1382 return FALSE;
1383
1384 switch (event)
1385 {
1386 case META_PLUGIN_MINIMIZE:
1387 counter = &priv->minimize_in_progress;
1388 break;
1389 case META_PLUGIN_MAP:
1390 counter = &priv->map_in_progress;
1391 break;
1392 case META_PLUGIN_DESTROY:
1393 counter = &priv->destroy_in_progress;
1394 break;
1395 case META_PLUGIN_UNMAXIMIZE:
1396 case META_PLUGIN_MAXIMIZE:
1397 case META_PLUGIN_SWITCH_WORKSPACE:
1398 case META_PLUGIN_TILE:
1399 g_assert_not_reached ();
1400 break;
1401 }
1402
1403 g_assert (counter);
1404
1405 use_freeze_thaw = is_freeze_thaw_effect (event);
1406
1407 if (use_freeze_thaw)
1408 meta_window_actor_freeze (self);
1409
1410 (*counter)++;
1411
1412 if (!meta_plugin_manager_event_simple (compositor->plugin_mgr,
1413 self,
1414 event))
1415 {
1416 (*counter)--;
1417 if (use_freeze_thaw)
1418 meta_window_actor_thaw (self);
1419 return FALSE;
1420 }
1421
1422 return TRUE;
1423 }
1424
1425 static void
meta_window_actor_after_effects(MetaWindowActor * self)1426 meta_window_actor_after_effects (MetaWindowActor *self)
1427 {
1428 MetaWindowActorPrivate *priv = self->priv;
1429
1430 if (priv->needs_destroy)
1431 {
1432 clutter_actor_destroy (CLUTTER_ACTOR (self));
1433 return;
1434 }
1435
1436 meta_window_actor_sync_visibility (self);
1437 meta_window_actor_sync_actor_geometry (self, FALSE);
1438
1439 if (priv->needs_pixmap)
1440 clutter_actor_queue_redraw (priv->actor);
1441 }
1442
1443 LOCAL_SYMBOL void
meta_window_actor_effect_completed(MetaWindowActor * self,gulong event)1444 meta_window_actor_effect_completed (MetaWindowActor *self,
1445 gulong event)
1446 {
1447 MetaWindowActorPrivate *priv = self->priv;
1448
1449 /* NB: Keep in mind that when effects get completed it possible
1450 * that the corresponding MetaWindow may have be been destroyed.
1451 * In this case priv->window will == NULL */
1452
1453 switch (event)
1454 {
1455 case META_PLUGIN_MINIMIZE:
1456 {
1457 priv->minimize_in_progress--;
1458 if (priv->minimize_in_progress < 0)
1459 {
1460 g_warning ("Error in minimize accounting.");
1461 priv->minimize_in_progress = 0;
1462 }
1463 }
1464 break;
1465 case META_PLUGIN_MAP:
1466 /*
1467 * Make sure that the actor is at the correct place in case
1468 * the plugin fscked.
1469 */
1470 priv->map_in_progress--;
1471 priv->position_changed = TRUE;
1472 if (priv->map_in_progress < 0)
1473 {
1474 g_warning ("Error in map accounting.");
1475 priv->map_in_progress = 0;
1476 }
1477 break;
1478 case META_PLUGIN_DESTROY:
1479 priv->destroy_in_progress--;
1480
1481 if (priv->destroy_in_progress < 0)
1482 {
1483 g_warning ("Error in destroy accounting.");
1484 priv->destroy_in_progress = 0;
1485 }
1486 break;
1487 case META_PLUGIN_UNMAXIMIZE:
1488 priv->unmaximize_in_progress--;
1489 if (priv->unmaximize_in_progress < 0)
1490 {
1491 g_warning ("Error in unmaximize accounting.");
1492 priv->unmaximize_in_progress = 0;
1493 }
1494 break;
1495 case META_PLUGIN_MAXIMIZE:
1496 priv->maximize_in_progress--;
1497 if (priv->maximize_in_progress < 0)
1498 {
1499 g_warning ("Error in maximize accounting.");
1500 priv->maximize_in_progress = 0;
1501 }
1502 break;
1503 case META_PLUGIN_TILE:
1504 priv->tile_in_progress--;
1505 if (priv->tile_in_progress < 0)
1506 {
1507 g_warning ("Error in tile accounting.");
1508 priv->tile_in_progress = 0;
1509 }
1510 break;
1511 case META_PLUGIN_SWITCH_WORKSPACE:
1512 g_assert_not_reached ();
1513 break;
1514 }
1515
1516 if (is_freeze_thaw_effect (event))
1517 meta_window_actor_thaw (self);
1518
1519 if (!meta_window_actor_effect_in_progress (self))
1520 meta_window_actor_after_effects (self);
1521 }
1522
1523 /* Called to drop our reference to a window backing pixmap that we
1524 * previously obtained with XCompositeNameWindowPixmap. We do this
1525 * when the window is unmapped or when we want to update to a new
1526 * pixmap for a new size.
1527 */
1528 static void
meta_window_actor_detach(MetaWindowActor * self)1529 meta_window_actor_detach (MetaWindowActor *self)
1530 {
1531 MetaWindowActorPrivate *priv = self->priv;
1532 MetaScreen *screen = priv->screen;
1533 MetaDisplay *display = meta_screen_get_display (screen);
1534 Display *xdisplay = meta_display_get_xdisplay (display);
1535
1536 if (!priv->back_pixmap)
1537 return;
1538
1539 /* Get rid of all references to the pixmap before freeing it; it's unclear whether
1540 * you are supposed to be able to free a GLXPixmap after freeing the underlying
1541 * pixmap, but it certainly doesn't work with current DRI/Mesa
1542 */
1543 meta_shaped_texture_set_texture (META_SHAPED_TEXTURE (priv->actor), NULL);
1544
1545 cogl_flush();
1546
1547 XFreePixmap (xdisplay, priv->back_pixmap);
1548 priv->back_pixmap = None;
1549
1550 priv->needs_pixmap = TRUE;
1551 }
1552
1553 LOCAL_SYMBOL gboolean
meta_window_actor_should_unredirect(MetaWindowActor * self)1554 meta_window_actor_should_unredirect (MetaWindowActor *self)
1555 {
1556 MetaWindow *metaWindow = meta_window_actor_get_meta_window (self);
1557 MetaWindowActorPrivate *priv = self->priv;
1558
1559 if (meta_window_actor_is_destroyed (self))
1560 return FALSE;
1561
1562 if (meta_window_requested_dont_bypass_compositor (metaWindow))
1563 return FALSE;
1564
1565 if (priv->opacity != 0xff)
1566 return FALSE;
1567
1568 if (metaWindow->has_shape)
1569 return FALSE;
1570
1571 if (priv->argb32 && !meta_window_requested_bypass_compositor (metaWindow))
1572 return FALSE;
1573
1574 if (!meta_window_is_monitor_sized (metaWindow))
1575 return FALSE;
1576
1577 if (meta_window_requested_bypass_compositor (metaWindow))
1578 return TRUE;
1579
1580 if (meta_window_is_override_redirect (metaWindow))
1581 return TRUE;
1582
1583 if (priv->does_full_damage && meta_prefs_get_unredirect_fullscreen_windows ())
1584 return TRUE;
1585
1586 return FALSE;
1587 }
1588
1589 LOCAL_SYMBOL void
meta_window_actor_set_redirected(MetaWindowActor * self,gboolean state)1590 meta_window_actor_set_redirected (MetaWindowActor *self, gboolean state)
1591 {
1592 MetaWindowActorPrivate *priv = self->priv;
1593 MetaWindow *metaWindow = priv->window;
1594 MetaDisplay *display = metaWindow->display;
1595
1596 Display *xdisplay = meta_display_get_xdisplay (display);
1597 Window xwin = meta_window_actor_get_x_window (self);
1598
1599 if (priv->unredirected != state)
1600 return;
1601
1602 meta_error_trap_push (display);
1603
1604 if (state)
1605 {
1606 XCompositeRedirectWindow (xdisplay, xwin, CompositeRedirectManual);
1607 meta_window_actor_detach (self);
1608 priv->unredirected = FALSE;
1609 }
1610 else
1611 {
1612 XCompositeUnredirectWindow (xdisplay, xwin, CompositeRedirectManual);
1613 priv->repaint_scheduled = TRUE;
1614 priv->unredirected = TRUE;
1615 }
1616
1617 meta_error_trap_pop (display);
1618 }
1619
1620 LOCAL_SYMBOL void
meta_window_actor_destroy(MetaWindowActor * self)1621 meta_window_actor_destroy (MetaWindowActor *self)
1622 {
1623 MetaWindow *window;
1624 MetaWindowActorPrivate *priv = self->priv;
1625 MetaWindowType window_type;
1626
1627 window = priv->window;
1628 window_type = meta_window_get_window_type (window);
1629 meta_window_set_compositor_private (window, NULL);
1630
1631 if (priv->send_frame_messages_timer != 0)
1632 {
1633 g_source_remove (priv->send_frame_messages_timer);
1634 priv->send_frame_messages_timer = 0;
1635 }
1636
1637 if (window_type == META_WINDOW_DROPDOWN_MENU ||
1638 window_type == META_WINDOW_POPUP_MENU ||
1639 window_type == META_WINDOW_TOOLTIP ||
1640 window_type == META_WINDOW_NOTIFICATION ||
1641 window_type == META_WINDOW_COMBO ||
1642 window_type == META_WINDOW_DND ||
1643 window_type == META_WINDOW_OVERRIDE_OTHER)
1644 {
1645 /*
1646 * No effects, just kill it.
1647 */
1648 clutter_actor_destroy (CLUTTER_ACTOR (self));
1649 return;
1650 }
1651
1652 priv->needs_destroy = TRUE;
1653
1654 if (!meta_window_actor_effect_in_progress (self))
1655 clutter_actor_destroy (CLUTTER_ACTOR (self));
1656 }
1657
1658 LOCAL_SYMBOL void
meta_window_actor_sync_actor_geometry(MetaWindowActor * self,gboolean did_placement)1659 meta_window_actor_sync_actor_geometry (MetaWindowActor *self,
1660 gboolean did_placement)
1661 {
1662 MetaWindowActorPrivate *priv = self->priv;
1663 MetaRectangle window_rect;
1664
1665 meta_window_get_input_rect (priv->window, &window_rect);
1666
1667 if (priv->last_width != window_rect.width ||
1668 priv->last_height != window_rect.height)
1669 {
1670 priv->size_changed = TRUE;
1671 priv->last_width = window_rect.width;
1672 priv->last_height = window_rect.height;
1673 }
1674
1675 if (priv->last_x != window_rect.x ||
1676 priv->last_y != window_rect.y)
1677 {
1678 priv->position_changed = TRUE;
1679 priv->last_x = window_rect.x;
1680 priv->last_y = window_rect.y;
1681 }
1682
1683 /* Normally we want freezing a window to also freeze its position; this allows
1684 * windows to atomically move and resize together, either under app control,
1685 * or because the user is resizing from the left/top. But on initial placement
1686 * we need to assign a position, since immediately after the window
1687 * is shown, the map effect will go into effect and prevent further geometry
1688 * updates.
1689 */
1690 if (is_frozen (self) && !did_placement)
1691 return;
1692
1693 if (meta_window_actor_effect_in_progress (self))
1694 return;
1695
1696 if (priv->size_changed)
1697 {
1698 priv->needs_pixmap = TRUE;
1699 meta_window_actor_update_shape (self);
1700
1701 clutter_actor_set_size (CLUTTER_ACTOR (self),
1702 window_rect.width, window_rect.height);
1703 }
1704
1705 if (priv->position_changed)
1706 {
1707 clutter_actor_set_position (CLUTTER_ACTOR (self),
1708 window_rect.x, window_rect.y);
1709 }
1710 }
1711
1712 void
meta_window_actor_show(MetaWindowActor * self,MetaCompEffect effect)1713 meta_window_actor_show (MetaWindowActor *self,
1714 MetaCompEffect effect)
1715 {
1716 MetaWindowActorPrivate *priv = self->priv;
1717 gulong event;
1718
1719 g_return_if_fail (!priv->visible);
1720
1721 priv->visible = TRUE;
1722
1723 event = 0;
1724 switch (effect)
1725 {
1726 case META_COMP_EFFECT_CREATE:
1727 event = META_PLUGIN_MAP;
1728 break;
1729 case META_COMP_EFFECT_UNMINIMIZE:
1730 /* FIXME: should have META_PLUGIN_UNMINIMIZE */
1731 event = META_PLUGIN_MAP;
1732 break;
1733 case META_COMP_EFFECT_NONE:
1734 break;
1735 case META_COMP_EFFECT_DESTROY:
1736 case META_COMP_EFFECT_MINIMIZE:
1737 g_assert_not_reached();
1738 }
1739
1740 if (priv->redecorating ||
1741 priv->screen->display->compositor->switch_workspace_in_progress ||
1742 event == 0 ||
1743 !meta_prefs_get_desktop_effects () ||
1744 !start_simple_effect (self, event))
1745 {
1746 clutter_actor_show (CLUTTER_ACTOR (self));
1747 priv->redecorating = FALSE;
1748 }
1749 }
1750
1751 LOCAL_SYMBOL void
meta_window_actor_hide(MetaWindowActor * self,MetaCompEffect effect)1752 meta_window_actor_hide (MetaWindowActor *self,
1753 MetaCompEffect effect)
1754 {
1755 MetaWindowActorPrivate *priv = self->priv;
1756 MetaCompositor *compositor = priv->screen->display->compositor;
1757 gulong event;
1758
1759 g_return_if_fail (priv->visible || (!priv->visible && meta_window_is_attached_dialog (priv->window)));
1760
1761 priv->visible = FALSE;
1762
1763 /* If a plugin is animating a workspace transition, we have to
1764 * hold off on hiding the window, and do it after the workspace
1765 * switch completes
1766 */
1767 if (compositor->switch_workspace_in_progress)
1768 return;
1769
1770 event = 0;
1771 switch (effect)
1772 {
1773 case META_COMP_EFFECT_DESTROY:
1774 event = META_PLUGIN_DESTROY;
1775 break;
1776 case META_COMP_EFFECT_MINIMIZE:
1777 event = META_PLUGIN_MINIMIZE;
1778 break;
1779 case META_COMP_EFFECT_NONE:
1780 break;
1781 case META_COMP_EFFECT_UNMINIMIZE:
1782 case META_COMP_EFFECT_CREATE:
1783 g_assert_not_reached();
1784 }
1785
1786 if (event == 0 ||
1787 !meta_prefs_get_desktop_effects () ||
1788 !start_simple_effect (self, event))
1789 clutter_actor_hide (CLUTTER_ACTOR (self));
1790 }
1791
1792 LOCAL_SYMBOL void
meta_window_actor_maximize(MetaWindowActor * self,MetaRectangle * old_rect,MetaRectangle * new_rect)1793 meta_window_actor_maximize (MetaWindowActor *self,
1794 MetaRectangle *old_rect,
1795 MetaRectangle *new_rect)
1796 {
1797 MetaCompositor *compositor = self->priv->screen->display->compositor;
1798 /* The window has already been resized (in order to compute new_rect),
1799 * which by side effect caused the actor to be resized. Restore it to the
1800 * old size and position */
1801 clutter_actor_set_position (CLUTTER_ACTOR (self), old_rect->x, old_rect->y);
1802 clutter_actor_set_size (CLUTTER_ACTOR (self), old_rect->width, old_rect->height);
1803
1804 self->priv->maximize_in_progress++;
1805 meta_window_actor_freeze (self);
1806
1807 if (!compositor->plugin_mgr ||
1808 !meta_plugin_manager_event_maximize (compositor->plugin_mgr,
1809 self,
1810 META_PLUGIN_MAXIMIZE,
1811 new_rect->x, new_rect->y,
1812 new_rect->width, new_rect->height))
1813
1814 {
1815 self->priv->maximize_in_progress--;
1816 meta_window_actor_thaw (self);
1817 }
1818 }
1819
1820 LOCAL_SYMBOL void
meta_window_actor_unmaximize(MetaWindowActor * self,MetaRectangle * old_rect,MetaRectangle * new_rect)1821 meta_window_actor_unmaximize (MetaWindowActor *self,
1822 MetaRectangle *old_rect,
1823 MetaRectangle *new_rect)
1824 {
1825 MetaCompositor *compositor = self->priv->screen->display->compositor;
1826
1827 /* The window has already been resized (in order to compute new_rect),
1828 * which by side effect caused the actor to be resized. Restore it to the
1829 * old size and position */
1830 clutter_actor_set_position (CLUTTER_ACTOR (self), old_rect->x, old_rect->y);
1831 clutter_actor_set_size (CLUTTER_ACTOR (self), old_rect->width, old_rect->height);
1832
1833 self->priv->unmaximize_in_progress++;
1834 meta_window_actor_freeze (self);
1835
1836 if (!compositor->plugin_mgr ||
1837 !meta_plugin_manager_event_maximize (compositor->plugin_mgr,
1838 self,
1839 META_PLUGIN_UNMAXIMIZE,
1840 new_rect->x, new_rect->y,
1841 new_rect->width, new_rect->height))
1842 {
1843 self->priv->unmaximize_in_progress--;
1844 meta_window_actor_thaw (self);
1845 }
1846 }
1847
1848 LOCAL_SYMBOL void
meta_window_actor_tile(MetaWindowActor * self,MetaRectangle * old_rect,MetaRectangle * new_rect)1849 meta_window_actor_tile (MetaWindowActor *self,
1850 MetaRectangle *old_rect,
1851 MetaRectangle *new_rect)
1852 {
1853 MetaCompositor *compositor = self->priv->screen->display->compositor;
1854
1855 /* The window has already been resized (in order to compute new_rect),
1856 * which by side effect caused the actor to be resized. Restore it to the
1857 * old size and position */
1858 clutter_actor_set_position (CLUTTER_ACTOR (self), old_rect->x, old_rect->y);
1859 clutter_actor_set_size (CLUTTER_ACTOR (self), old_rect->width, old_rect->height);
1860
1861 self->priv->tile_in_progress++;
1862 meta_window_actor_freeze (self);
1863
1864 if (!compositor->plugin_mgr ||
1865 !meta_plugin_manager_event_maximize (compositor->plugin_mgr,
1866 self,
1867 META_PLUGIN_TILE,
1868 new_rect->x, new_rect->y,
1869 new_rect->width, new_rect->height))
1870
1871 {
1872 self->priv->tile_in_progress--;
1873 meta_window_actor_thaw (self);
1874 }
1875 }
1876
1877 static void
ensure_tooltip_visible(MetaWindow * window)1878 ensure_tooltip_visible (MetaWindow *window)
1879 {
1880 const MetaMonitorInfo *wmonitor;
1881 MetaRectangle work_area, win_rect;
1882 gint new_x, new_y;
1883
1884 /* Why this is here:
1885 * As of gtk 3.24, tooltips for GtkStatusIcons began displaying their tool tip
1886 * off the screen in certain situations.
1887 *
1888 * See: https://github.com/GNOME/gtk/commit/14d22cb3233e
1889 *
1890 * If the status icon is too small relative to its panel (which has been assigned
1891 * as a strut to muffin), tooltip positioning fails both tests in gdkwindowimpl.c
1892 * (maybe_flip_position()) skipping repositioning of the tooltip inside the workarea.
1893 * This only occurs on bottom panels, and only begins happening when the status icon
1894 * becomes 10px or more smaller than the panel it's *centered* on.
1895 *
1896 * Since the calculations are based upon the monitor's workarea and the status icon
1897 * plug window's size, there's no way to compensate for or fool gtk into displaying it
1898 * correctly. So here, we do our own check and adjustment if a part of the tooltip
1899 * window falls outside the current monitor's work area. This is also useful since
1900 * muffin knows *exactly* the work area for each monitor, whereas gtk only has
1901 * _NET_WORKAREA to go by, which only keeps track of (primary monitor * n_workspaces),
1902 * so an odd monitor layout would trip this up anyhow.
1903 *
1904 * This may cause regressions - see: https://github.com/linuxmint/muffin/commit/050038690,
1905 * but without being able to reproduce the issue mentioned there, we'll just have to address
1906 * it if it appears again as a result of this change. */
1907
1908 wmonitor = meta_screen_get_monitor_for_window (window->screen, window);
1909
1910 meta_workspace_get_work_area_for_monitor (meta_screen_get_active_workspace (window->screen),
1911 wmonitor->number,
1912 &work_area);
1913
1914 meta_window_get_outer_rect (window, &win_rect);
1915
1916 new_x = win_rect.x;
1917 new_y = win_rect.y;
1918
1919 if (win_rect.y < work_area.y)
1920 {
1921 new_y = work_area.y;
1922 }
1923 else if (win_rect.y + win_rect.height > work_area.y + work_area.height)
1924 {
1925 new_y = (work_area.y + work_area.height - win_rect.height);
1926 }
1927
1928 if (win_rect.x < work_area.x)
1929 {
1930 new_x = work_area.x;
1931 }
1932 else if (win_rect.x + win_rect.width > work_area.x + work_area.width)
1933 {
1934 new_x = (work_area.x + work_area.width - win_rect.width);
1935 }
1936
1937 if (new_x != win_rect.x || new_y != win_rect.y)
1938 {
1939 meta_window_move(window, FALSE, new_x, new_y);
1940 }
1941 }
1942
1943 LOCAL_SYMBOL MetaWindowActor *
meta_window_actor_new(MetaWindow * window)1944 meta_window_actor_new (MetaWindow *window)
1945 {
1946 MetaScreen *screen = window->screen;
1947 MetaCompositor *compositor = screen->display->compositor;
1948 MetaWindowActor *self;
1949 MetaWindowActorPrivate *priv;
1950 MetaFrame *frame;
1951 Window top_window;
1952 ClutterActor *window_group;
1953
1954 frame = meta_window_get_frame (window);
1955 if (frame)
1956 top_window = meta_frame_get_xwindow (frame);
1957 else
1958 top_window = meta_window_get_xwindow (window);
1959
1960 meta_verbose ("add window: Meta %p, xwin 0x%x\n", window, (guint)top_window);
1961
1962 self = g_object_new (META_TYPE_WINDOW_ACTOR,
1963 "meta-window", window,
1964 "x-window", top_window,
1965 "meta-screen", screen,
1966 NULL);
1967
1968 priv = self->priv;
1969
1970 priv->last_width = -1;
1971 priv->last_height = -1;
1972 priv->last_x = -1;
1973 priv->last_y = -1;
1974
1975 priv->needs_pixmap = TRUE;
1976
1977 meta_window_actor_set_updates_frozen (self,
1978 meta_window_updates_are_frozen (priv->window));
1979
1980 /* If a window doesn't start off with updates frozen, we should
1981 * we should send a _NET_WM_FRAME_DRAWN immediately after the first drawn.
1982 */
1983 if (priv->window->extended_sync_request_counter && !priv->updates_frozen)
1984 meta_window_actor_queue_frame_drawn (self, FALSE);
1985
1986 meta_window_actor_sync_actor_geometry (self, priv->window->placed);
1987
1988 /* Hang our compositor window state off the MetaWindow for fast retrieval */
1989 meta_window_set_compositor_private (window, G_OBJECT (self));
1990
1991 if (window->type == META_WINDOW_DND)
1992 window_group = compositor->window_group;
1993 else if (window->layer == META_LAYER_OVERRIDE_REDIRECT)
1994 {
1995 if (window->type == META_WINDOW_TOOLTIP)
1996 {
1997 ensure_tooltip_visible (window);
1998 }
1999
2000 window_group = compositor->top_window_group;
2001 }
2002 else if (window->type == META_WINDOW_DESKTOP)
2003 window_group = compositor->bottom_window_group;
2004 else
2005 window_group = compositor->window_group;
2006
2007 clutter_actor_add_child (window_group, CLUTTER_ACTOR (self));
2008
2009 clutter_actor_hide (CLUTTER_ACTOR (self));
2010
2011 /* Initial position in the stack is arbitrary; stacking will be synced
2012 * before we first paint.
2013 */
2014 compositor->windows = g_list_append (compositor->windows, self);
2015
2016 return self;
2017 }
2018
2019 static void
meta_window_actor_update_shape_region(MetaWindowActor * self,cairo_region_t * region)2020 meta_window_actor_update_shape_region (MetaWindowActor *self,
2021 cairo_region_t *region)
2022 {
2023 MetaWindowActorPrivate *priv = self->priv;
2024
2025 g_clear_pointer (&priv->shape_region, cairo_region_destroy);
2026
2027 /* region must be non-null */
2028 priv->shape_region = region;
2029 cairo_region_reference (region);
2030 }
2031
2032 /**
2033 * meta_window_actor_get_obscured_region:
2034 * @self: a #MetaWindowActor
2035 *
2036 * Gets the region that is completely obscured by the window. Coordinates
2037 * are relative to the upper-left of the window.
2038 *
2039 * Return value: (transfer none): the area obscured by the window,
2040 * %NULL is the same as an empty region.
2041 */
2042 LOCAL_SYMBOL cairo_region_t *
meta_window_actor_get_obscured_region(MetaWindowActor * self)2043 meta_window_actor_get_obscured_region (MetaWindowActor *self)
2044 {
2045 MetaWindowActorPrivate *priv = self->priv;
2046
2047 if (priv->back_pixmap && priv->opacity == 0xff)
2048 return priv->opaque_region;
2049 else
2050 return NULL;
2051 }
2052
2053 #if 0
2054 /* Print out a region; useful for debugging */
2055 static void
2056 dump_region (cairo_region_t *region)
2057 {
2058 int n_rects;
2059 int i;
2060
2061 n_rects = cairo_region_num_rectangles (region);
2062 g_print ("[");
2063 for (i = 0; i < n_rects; i++)
2064 {
2065 cairo_rectangle_int_t rect;
2066 cairo_region_get_rectangle (region, i, &rect);
2067 g_print ("+%d+%dx%dx%d ",
2068 rect.x, rect.y, rect.width, rect.height);
2069 }
2070 g_print ("]\n");
2071 }
2072 #endif
2073
2074 /**
2075 * meta_window_actor_set_unobscured_region:
2076 * @self: a #MetaWindowActor
2077 * @unobscured_region: the region of the screen that isn't completely
2078 * obscured.
2079 *
2080 * Provides a hint as to what areas of the window need to queue
2081 * redraws when damaged. Regions not in @unobscured_region are completely obscured.
2082 * Unlike meta_window_actor_set_clip_region(), the region here
2083 * doesn't take into account any clipping that is in effect while drawing.
2084 */
2085 void
meta_window_actor_set_unobscured_region(MetaWindowActor * self,cairo_region_t * unobscured_region)2086 meta_window_actor_set_unobscured_region (MetaWindowActor *self,
2087 cairo_region_t *unobscured_region)
2088 {
2089 MetaWindowActorPrivate *priv = self->priv;
2090
2091 if (priv->unobscured_region)
2092 cairo_region_destroy (priv->unobscured_region);
2093
2094 if (unobscured_region)
2095 priv->unobscured_region = cairo_region_copy (unobscured_region);
2096 else
2097 priv->unobscured_region = NULL;
2098 }
2099
2100 /**
2101 * meta_window_actor_set_visible_region:
2102 * @self: a #MetaWindowActor
2103 * @visible_region: the region of the screen that isn't completely
2104 * obscured.
2105 *
2106 * Provides a hint as to what areas of the window need to be
2107 * drawn. Regions not in @visible_region are completely obscured.
2108 * This will be set before painting then unset afterwards.
2109 */
2110 LOCAL_SYMBOL void
meta_window_actor_set_visible_region(MetaWindowActor * self,cairo_region_t * visible_region)2111 meta_window_actor_set_visible_region (MetaWindowActor *self,
2112 cairo_region_t *visible_region)
2113 {
2114 MetaWindowActorPrivate *priv = self->priv;
2115
2116 meta_shaped_texture_set_clip_region (META_SHAPED_TEXTURE (priv->actor),
2117 visible_region);
2118 }
2119
2120 /**
2121 * meta_window_actor_set_visible_region_beneath:
2122 * @self: a #MetaWindowActor
2123 * @visible_region: the region of the screen that isn't completely
2124 * obscured beneath the main window texture.
2125 *
2126 * Provides a hint as to what areas need to be drawn *beneath*
2127 * the main window texture. This is the relevant visible region
2128 * when drawing the shadow, properly accounting for areas of the
2129 * shadow hid by the window itself. This will be set before painting
2130 * then unset afterwards.
2131 */
2132 LOCAL_SYMBOL void
meta_window_actor_set_visible_region_beneath(MetaWindowActor * self,cairo_region_t * beneath_region)2133 meta_window_actor_set_visible_region_beneath (MetaWindowActor *self,
2134 cairo_region_t *beneath_region)
2135 {
2136 MetaWindowActorPrivate *priv = self->priv;
2137 gboolean appears_focused = meta_window_appears_focused (priv->window);
2138
2139 if (appears_focused ? priv->focused_shadow : priv->unfocused_shadow)
2140 {
2141 g_clear_pointer (&priv->shadow_clip, cairo_region_destroy);
2142 priv->shadow_clip = cairo_region_copy (beneath_region);
2143
2144 if (clip_shadow_under_window (self))
2145 cairo_region_subtract (priv->shadow_clip,
2146 meta_window_get_frame_bounds (priv->window));
2147 }
2148 }
2149
2150 /**
2151 * meta_window_actor_reset_visible_regions:
2152 * @self: a #MetaWindowActor
2153 *
2154 * Unsets the regions set by meta_window_actor_reset_visible_region() and
2155 * meta_window_actor_reset_visible_region_beneath()
2156 */
2157 LOCAL_SYMBOL void
meta_window_actor_reset_visible_regions(MetaWindowActor * self)2158 meta_window_actor_reset_visible_regions (MetaWindowActor *self)
2159 {
2160 MetaWindowActorPrivate *priv = self->priv;
2161
2162 meta_shaped_texture_set_clip_region (META_SHAPED_TEXTURE (priv->actor),
2163 NULL);
2164 g_clear_pointer (&priv->shadow_clip, cairo_region_destroy);
2165 }
2166
2167 static void
check_needs_pixmap(MetaWindowActor * self)2168 check_needs_pixmap (MetaWindowActor *self)
2169 {
2170 MetaWindowActorPrivate *priv = self->priv;
2171 MetaScreen *screen = priv->screen;
2172 MetaDisplay *display = screen->display;
2173 Display *xdisplay = display->xdisplay;
2174 MetaCompositor *compositor = display->compositor;
2175 Window xwindow = priv->xwindow;
2176
2177 if ((!priv->window->mapped && !priv->window->shaded) || !priv->needs_pixmap)
2178 return;
2179
2180 if (xwindow == screen->xroot ||
2181 xwindow == clutter_x11_get_stage_window (compositor->stage))
2182 return;
2183
2184 if (priv->size_changed)
2185 {
2186 meta_window_actor_detach (self);
2187 priv->size_changed = FALSE;
2188 }
2189
2190 if (priv->position_changed)
2191 priv->position_changed = FALSE;
2192
2193 meta_error_trap_push (display);
2194
2195 if (priv->back_pixmap == None)
2196 {
2197 CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
2198 CoglTexture *texture;
2199
2200 meta_error_trap_push (display);
2201
2202 priv->back_pixmap = XCompositeNameWindowPixmap (xdisplay, xwindow);
2203
2204 if (meta_error_trap_pop_with_return (display) != Success)
2205 {
2206 /* Probably a BadMatch if the window isn't viewable; we could
2207 * GrabServer/GetWindowAttributes/NameWindowPixmap/UngrabServer/Sync
2208 * to avoid this, but there's no reason to take two round trips
2209 * when one will do. (We need that Sync if we want to handle failures
2210 * for any reason other than !viewable. That's unlikely, but maybe
2211 * we'll BadAlloc or something.)
2212 */
2213 priv->back_pixmap = None;
2214 }
2215
2216 if (priv->back_pixmap == None)
2217 {
2218 meta_verbose ("Unable to get named pixmap for %p\n", self);
2219 goto out;
2220 }
2221
2222 if (compositor->no_mipmaps)
2223 meta_shaped_texture_set_create_mipmaps (META_SHAPED_TEXTURE (priv->actor),
2224 FALSE);
2225
2226 texture = COGL_TEXTURE (cogl_texture_pixmap_x11_new (ctx, priv->back_pixmap, FALSE, NULL));
2227
2228 /*
2229 * This only works *after* actually setting the pixmap, so we have to
2230 * do it here.
2231 * See: http://bugzilla.clutter-project.org/show_bug.cgi?id=2236
2232 */
2233 if (G_UNLIKELY (!cogl_texture_pixmap_x11_is_using_tfp_extension (texture)))
2234 g_warning ("NOTE: Not using GLX TFP!\n");
2235
2236 meta_shaped_texture_set_texture (META_SHAPED_TEXTURE (priv->actor), texture);
2237 }
2238
2239 priv->needs_pixmap = FALSE;
2240
2241 out:
2242 meta_error_trap_pop (display);
2243 }
2244
2245 static void
check_needs_shadow(MetaWindowActor * self)2246 check_needs_shadow (MetaWindowActor *self)
2247 {
2248 if (!self->priv->window->display->shadows_enabled) {
2249 return;
2250 }
2251
2252 MetaWindowActorPrivate *priv = self->priv;
2253 MetaShadow *old_shadow = NULL;
2254 MetaShadow **shadow_location;
2255 gboolean recompute_shadow;
2256 gboolean should_have_shadow;
2257 gboolean appears_focused;
2258
2259 if (!priv->window->mapped && !priv->window->shaded)
2260 return;
2261
2262 /* Calling meta_window_actor_has_shadow() here at every pre-paint is cheap
2263 * and avoids the need to explicitly handle window type changes, which
2264 * we would do if tried to keep track of when we might be adding or removing
2265 * a shadow more explicitly. We only keep track of changes to the *shape* of
2266 * the shadow with priv->recompute_shadow.
2267 */
2268
2269 should_have_shadow = meta_window_actor_has_shadow (self);
2270 priv->should_have_shadow = should_have_shadow;
2271 appears_focused = meta_window_appears_focused (priv->window);
2272
2273 if (appears_focused)
2274 {
2275 recompute_shadow = priv->recompute_focused_shadow;
2276 priv->recompute_focused_shadow = FALSE;
2277 shadow_location = &priv->focused_shadow;
2278 }
2279 else
2280 {
2281 recompute_shadow = priv->recompute_unfocused_shadow;
2282 priv->recompute_unfocused_shadow = FALSE;
2283 shadow_location = &priv->unfocused_shadow;
2284 }
2285
2286 if (!should_have_shadow || recompute_shadow)
2287 {
2288 if (*shadow_location != NULL)
2289 {
2290 old_shadow = *shadow_location;
2291 *shadow_location = NULL;
2292 }
2293 }
2294
2295 if (*shadow_location == NULL && should_have_shadow)
2296 {
2297 if (priv->shadow_shape == NULL)
2298 {
2299 if (priv->shape_region)
2300 priv->shadow_shape = meta_window_shape_new (priv->shape_region);
2301 }
2302
2303 if (priv->shadow_shape != NULL)
2304 {
2305 MetaShadowFactory *factory = meta_shadow_factory_get_default ();
2306 const char *shadow_class = meta_window_actor_get_shadow_class (self);
2307 cairo_rectangle_int_t shape_bounds;
2308
2309 meta_window_actor_get_shape_bounds (self, &shape_bounds);
2310 *shadow_location = meta_shadow_factory_get_shadow (factory,
2311 priv->shadow_shape,
2312 shape_bounds.width, shape_bounds.height,
2313 shadow_class, appears_focused);
2314 }
2315 }
2316
2317 if (old_shadow != NULL)
2318 meta_shadow_unref (old_shadow);
2319 }
2320
2321 LOCAL_SYMBOL void
meta_window_actor_process_damage(MetaWindowActor * self,XDamageNotifyEvent * event)2322 meta_window_actor_process_damage (MetaWindowActor *self,
2323 XDamageNotifyEvent *event)
2324 {
2325 MetaWindowActorPrivate *priv = self->priv;
2326 MetaCompositor *compositor = priv->window->display->compositor;
2327
2328 priv->received_damage = TRUE;
2329
2330 /* Drop damage event for unredirected windows */
2331 if (priv->unredirected)
2332 return;
2333
2334 if (meta_window_is_fullscreen (priv->window) && g_list_last (compositor->windows)->data == self)
2335 {
2336 MetaRectangle window_rect;
2337 meta_window_get_outer_rect (priv->window, &window_rect);
2338
2339 if (event->area.x == 0 &&
2340 event->area.y == 0 &&
2341 window_rect.width == event->area.width &&
2342 window_rect.height == event->area.height)
2343 priv->full_damage_frames_count++;
2344 else
2345 priv->full_damage_frames_count = 0;
2346
2347 if (priv->full_damage_frames_count >= 100)
2348 priv->does_full_damage = TRUE;
2349 }
2350
2351 if (priv->freeze_count)
2352 {
2353 /* The window is frozen due to an effect in progress: we ignore damage
2354 * here on the off chance that this will stop the corresponding
2355 * texture_from_pixmap from being update.
2356 *
2357 * needs_damage_all tracks that some unknown damage happened while the
2358 * window was frozen so that when the window becomes unfrozen we can
2359 * issue a full window update to cover any lost damage.
2360 *
2361 * It should be noted that this is an unreliable mechanism since it's
2362 * quite likely that drivers will aim to provide a zero-copy
2363 * implementation of the texture_from_pixmap extension and in those cases
2364 * any drawing done to the window is always immediately reflected in the
2365 * texture regardless of damage event handling.
2366 */
2367 priv->needs_damage_all = TRUE;
2368 return;
2369 }
2370
2371 if (!priv->window->mapped || priv->needs_pixmap)
2372 return;
2373
2374 update_area (self, event->area.x, event->area.y, event->area.width, event->area.height);
2375 priv->repaint_scheduled = meta_shaped_texture_update_area (META_SHAPED_TEXTURE (priv->actor),
2376 event->area.x,
2377 event->area.y,
2378 event->area.width,
2379 event->area.height,
2380 clutter_actor_has_mapped_clones (priv->actor) ? NULL : priv->unobscured_region);
2381 }
2382
2383 LOCAL_SYMBOL void
meta_window_actor_sync_visibility(MetaWindowActor * self)2384 meta_window_actor_sync_visibility (MetaWindowActor *self)
2385 {
2386 MetaWindowActorPrivate *priv = self->priv;
2387
2388 if (CLUTTER_ACTOR_IS_VISIBLE (self) != priv->visible)
2389 {
2390 if (priv->visible)
2391 clutter_actor_show (CLUTTER_ACTOR (self));
2392 else
2393 clutter_actor_hide (CLUTTER_ACTOR (self));
2394 }
2395 }
2396
2397 static inline void
set_integral_bounding_rect(cairo_rectangle_int_t * rect,double x,double y,double width,double height)2398 set_integral_bounding_rect (cairo_rectangle_int_t *rect,
2399 double x, double y,
2400 double width, double height)
2401 {
2402 rect->x = floor(x);
2403 rect->y = floor(y);
2404 rect->width = ceil(x + width) - rect->x;
2405 rect->height = ceil(y + height) - rect->y;
2406 }
2407
2408 static void
update_corners(MetaWindowActor * self)2409 update_corners (MetaWindowActor *self)
2410 {
2411 MetaWindowActorPrivate *priv = self->priv;
2412 MetaRectangle outer = priv->window->frame->rect;
2413 MetaFrameBorders borders;
2414 cairo_rectangle_int_t corner_rects[4];
2415 cairo_region_t *corner_region;
2416 cairo_path_t *corner_path;
2417 float top_left, top_right, bottom_left, bottom_right;
2418 float x, y;
2419
2420 /* need these to build a path */
2421 cairo_t *cr;
2422 cairo_surface_t *surface;
2423
2424 meta_frame_calc_borders (priv->window->frame, &borders);
2425
2426 outer.width -= borders.invisible.left + borders.invisible.right;
2427 outer.height -= borders.invisible.top + borders.invisible.bottom;
2428
2429 meta_frame_get_corner_radiuses (priv->window->frame,
2430 &top_left,
2431 &top_right,
2432 &bottom_left,
2433 &bottom_right);
2434
2435 /* Unfortunately, cairo does not allow us to create a context
2436 * without a surface. Create a 0x0 image surface to "paint to"
2437 * so we can get the path. */
2438 surface = cairo_image_surface_create (CAIRO_FORMAT_A8,
2439 0, 0);
2440
2441 cr = cairo_create (surface);
2442
2443 /* top left */
2444 x = borders.invisible.left;
2445 y = borders.invisible.top;
2446
2447 set_integral_bounding_rect (&corner_rects[0],
2448 x, y, top_left, top_left);
2449
2450 cairo_arc (cr,
2451 x + top_left,
2452 y + top_left,
2453 top_left,
2454 0, M_PI*2);
2455
2456
2457 /* top right */
2458 x = x + outer.width - top_right;
2459
2460 set_integral_bounding_rect (&corner_rects[1],
2461 x, y, top_right, top_right);
2462
2463 cairo_arc (cr,
2464 x,
2465 y + top_right,
2466 top_right,
2467 0, M_PI*2);
2468
2469 /* bottom right */
2470 x = borders.invisible.left + outer.width - bottom_right;
2471 y = y + outer.height - bottom_right;
2472
2473 set_integral_bounding_rect (&corner_rects[2],
2474 x, y, bottom_right, bottom_right);
2475
2476 cairo_arc (cr,
2477 x,
2478 y,
2479 bottom_right,
2480 0, M_PI*2);
2481
2482 /* bottom left */
2483 x = borders.invisible.left;
2484 y = borders.invisible.top + outer.height - bottom_left;
2485
2486 set_integral_bounding_rect (&corner_rects[3],
2487 x, y, bottom_left, bottom_left);
2488
2489 cairo_arc (cr,
2490 x + bottom_left,
2491 y,
2492 bottom_left,
2493 0, M_PI*2);
2494
2495 corner_path = cairo_copy_path (cr);
2496
2497 cairo_surface_destroy (surface);
2498 cairo_destroy (cr);
2499
2500 corner_region = cairo_region_create_rectangles (corner_rects, 4);
2501
2502 meta_shaped_texture_set_overlay_path (META_SHAPED_TEXTURE (priv->actor),
2503 corner_region, corner_path);
2504
2505
2506 cairo_region_destroy (corner_region);
2507 }
2508
2509 static void
check_needs_reshape(MetaWindowActor * self)2510 check_needs_reshape (MetaWindowActor *self)
2511 {
2512 MetaWindowActorPrivate *priv = self->priv;
2513 MetaScreen *screen = priv->screen;
2514 MetaDisplay *display = meta_screen_get_display (screen);
2515 cairo_region_t *region = NULL;
2516 cairo_rectangle_int_t client_area;
2517 gboolean full_mask_reset = priv->window->fullscreen;
2518
2519 if ((!priv->window->mapped && !priv->window->shaded) || !priv->needs_reshape)
2520 return;
2521
2522 g_clear_pointer (&priv->shape_region, cairo_region_destroy);
2523 g_clear_pointer (&priv->shadow_shape, meta_window_shape_unref);
2524 g_clear_pointer (&priv->opaque_region, cairo_region_destroy);
2525
2526 meta_window_get_client_area_rect (priv->window, &client_area);
2527
2528 if (priv->window->frame)
2529 region = meta_window_get_frame_bounds (priv->window);
2530
2531 if (region != NULL)
2532 {
2533 /* This returns the window's internal frame bounds region,
2534 * so we need to copy it because we modify it below. */
2535 region = cairo_region_copy (region);
2536 }
2537 else
2538 {
2539 /* If we have no region, we have no frame. We have no frame,
2540 * so just use the client_area instead */
2541 region = cairo_region_create_rectangle (&client_area);
2542 }
2543
2544 #ifdef HAVE_SHAPE
2545 if (priv->window->has_shape)
2546 {
2547 Display *xdisplay = meta_display_get_xdisplay (display);
2548 XRectangle *rects;
2549 int n_rects, ordering;
2550
2551 /* Punch out client area. */
2552 cairo_region_subtract_rectangle (region, &client_area);
2553
2554 meta_error_trap_push (display);
2555 rects = XShapeGetRectangles (xdisplay,
2556 priv->window->xwindow,
2557 ShapeBounding,
2558 &n_rects,
2559 &ordering);
2560 meta_error_trap_pop (display);
2561
2562 if (rects)
2563 {
2564 int i;
2565 for (i = 0; i < n_rects; i ++)
2566 {
2567 cairo_rectangle_int_t rect = { rects[i].x + client_area.x,
2568 rects[i].y + client_area.y,
2569 rects[i].width,
2570 rects[i].height };
2571 cairo_region_union_rectangle (region, &rect);
2572 }
2573 XFree (rects);
2574 }
2575 }
2576 #endif
2577
2578 if (priv->argb32 && priv->window->opaque_region != NULL)
2579 {
2580 /* The opaque region is defined to be a part of the
2581 * window which ARGB32 will always paint with opaque
2582 * pixels. For these regions, we want to avoid painting
2583 * windows and shadows beneath them.
2584 *
2585 * If the client gives bad coordinates where it does not
2586 * fully paint, the behavior is defined by the specification
2587 * to be undefined, and considered a client bug. In mutter's
2588 * case, graphical glitches will occur.
2589 */
2590 priv->opaque_region = cairo_region_copy (priv->window->opaque_region);
2591 cairo_region_translate (priv->opaque_region, client_area.x, client_area.y);
2592 cairo_region_intersect (priv->opaque_region, region);
2593 }
2594 else if (priv->argb32)
2595 priv->opaque_region = NULL;
2596 else
2597 priv->opaque_region = cairo_region_reference (region);
2598
2599 if (priv->window->frame)
2600 update_corners (self);
2601 else if (priv->window->has_shape && priv->reshapes == 1)
2602 full_mask_reset = TRUE;
2603 else if (priv->reshapes < 2)
2604 priv->reshapes++;
2605
2606 if (priv->window->frame != NULL || priv->window->has_shape)
2607 meta_window_actor_reset_mask_texture (self, region, full_mask_reset);
2608
2609 meta_window_actor_update_shape_region (self, region);
2610
2611 cairo_region_destroy (region);
2612
2613 priv->needs_reshape = FALSE;
2614 meta_window_actor_invalidate_shadow (self);
2615 }
2616
2617 LOCAL_SYMBOL void
meta_window_actor_update_shape(MetaWindowActor * self)2618 meta_window_actor_update_shape (MetaWindowActor *self)
2619 {
2620 MetaWindowActorPrivate *priv = self->priv;
2621
2622 priv->needs_reshape = TRUE;
2623
2624 if (is_frozen (self))
2625 return;
2626
2627 clutter_actor_queue_redraw (priv->actor);
2628 }
2629
2630 LOCAL_SYMBOL void
meta_window_actor_handle_updates(MetaWindowActor * self)2631 meta_window_actor_handle_updates (MetaWindowActor *self)
2632 {
2633 MetaWindowActorPrivate *priv = self->priv;
2634 MetaScreen *screen = priv->screen;
2635 MetaDisplay *display = meta_screen_get_display (screen);
2636 Display *xdisplay = meta_display_get_xdisplay (display);
2637
2638 if (is_frozen (self))
2639 {
2640 /* The window is frozen due to a pending animation: we'll wait until
2641 * the animation finishes to reshape and repair the window */
2642 return;
2643 }
2644
2645 if (priv->unredirected)
2646 {
2647 /* Nothing to do here until/if the window gets redirected again */
2648 return;
2649 }
2650
2651 if (!priv->visible && !priv->needs_pixmap)
2652 return;
2653
2654 if (priv->received_damage)
2655 {
2656 meta_error_trap_push (display);
2657 XDamageSubtract (xdisplay, priv->damage, None, None);
2658 meta_error_trap_pop (display);
2659
2660 priv->received_damage = FALSE;
2661 }
2662
2663 check_needs_pixmap (self);
2664 check_needs_reshape (self);
2665 check_needs_shadow (self);
2666 }
2667
2668 void
meta_window_actor_pre_paint(MetaWindowActor * self)2669 meta_window_actor_pre_paint (MetaWindowActor *self)
2670 {
2671 if (meta_window_actor_is_destroyed (self))
2672 return;
2673
2674 meta_window_actor_handle_updates (self);
2675
2676 assign_frame_counter_to_frames (self);
2677 }
2678
2679 static void
do_send_frame_drawn(MetaWindowActor * self,FrameData * frame)2680 do_send_frame_drawn (MetaWindowActor *self, FrameData *frame)
2681 {
2682 MetaWindowActorPrivate *priv = self->priv;
2683 MetaDisplay *display = meta_screen_get_display (priv->screen);
2684 MetaWindow *window = meta_window_actor_get_meta_window (self);
2685 Display *xdisplay = meta_display_get_xdisplay (display);
2686
2687 XClientMessageEvent ev = { 0, };
2688
2689 frame->frame_drawn_time = meta_compositor_monotonic_time_to_server_time (display,
2690 g_get_monotonic_time ());
2691 priv->frame_drawn_time = frame->frame_drawn_time;
2692
2693 ev.type = ClientMessage;
2694 ev.window = meta_window_get_xwindow (window);
2695 ev.message_type = display->atom__NET_WM_FRAME_DRAWN;
2696 ev.format = 32;
2697 ev.data.l[0] = frame->sync_request_serial & G_GUINT64_CONSTANT(0xffffffff);
2698 ev.data.l[1] = frame->sync_request_serial >> 32;
2699 ev.data.l[2] = frame->frame_drawn_time & G_GUINT64_CONSTANT(0xffffffff);
2700 ev.data.l[3] = frame->frame_drawn_time >> 32;
2701
2702 meta_error_trap_push (display);
2703 XSendEvent (xdisplay, ev.window, False, 0, (XEvent*) &ev);
2704 XFlush (xdisplay);
2705 meta_error_trap_pop (display);
2706 }
2707
2708
2709 void
meta_window_actor_post_paint(MetaWindowActor * self)2710 meta_window_actor_post_paint (MetaWindowActor *self)
2711 {
2712 MetaWindowActorPrivate *priv = self->priv;
2713
2714 priv->repaint_scheduled = FALSE;
2715
2716 if (meta_window_actor_is_destroyed (self))
2717 return;
2718
2719 /* If the window had damage, but wasn't actually redrawn because
2720 * it is obscured, we should wait until timer expiration before
2721 * sending _NET_WM_FRAME_* messages.
2722 */
2723 if (priv->send_frame_messages_timer == 0 &&
2724 priv->needs_frame_drawn)
2725 {
2726 GList *l;
2727
2728 for (l = priv->frames; l; l = l->next)
2729 {
2730 FrameData *frame = l->data;
2731
2732 if (frame->frame_drawn_time == 0)
2733 do_send_frame_drawn (self, frame);
2734 }
2735
2736 priv->needs_frame_drawn = FALSE;
2737 }
2738 }
2739
2740 static void
do_send_frame_timings(MetaWindowActor * self,FrameData * frame,gint refresh_interval,gint64 presentation_time)2741 do_send_frame_timings (MetaWindowActor *self,
2742 FrameData *frame,
2743 gint refresh_interval,
2744 gint64 presentation_time)
2745 {
2746 MetaWindowActorPrivate *priv = self->priv;
2747 MetaDisplay *display = meta_screen_get_display (priv->screen);
2748 MetaWindow *window = meta_window_actor_get_meta_window (self);
2749 Display *xdisplay = meta_display_get_xdisplay (display);
2750
2751 XClientMessageEvent ev = { 0, };
2752
2753 ev.type = ClientMessage;
2754 ev.window = meta_window_get_xwindow (window);
2755 ev.message_type = display->atom__NET_WM_FRAME_TIMINGS;
2756 ev.format = 32;
2757 ev.data.l[0] = frame->sync_request_serial & G_GUINT64_CONSTANT(0xffffffff);
2758 ev.data.l[1] = frame->sync_request_serial >> 32;
2759
2760 if (presentation_time != 0)
2761 {
2762 gint64 presentation_time_server = meta_compositor_monotonic_time_to_server_time (display,
2763 presentation_time);
2764 gint64 presentation_time_offset = presentation_time_server - frame->frame_drawn_time;
2765 if (presentation_time_offset == 0)
2766 presentation_time_offset = 1;
2767
2768 if ((gint32)presentation_time_offset == presentation_time_offset)
2769 ev.data.l[2] = presentation_time_offset;
2770 }
2771
2772 ev.data.l[3] = refresh_interval;
2773 ev.data.l[4] = 1000 * META_SYNC_DELAY;
2774
2775 meta_error_trap_push (display);
2776 XSendEvent (xdisplay, ev.window, False, 0, (XEvent*) &ev);
2777 XFlush (xdisplay);
2778 meta_error_trap_pop (display);
2779 }
2780
2781 static void
send_frame_timings(MetaWindowActor * self,FrameData * frame,CoglFrameInfo * frame_info,gint64 presentation_time)2782 send_frame_timings (MetaWindowActor *self,
2783 FrameData *frame,
2784 CoglFrameInfo *frame_info,
2785 gint64 presentation_time)
2786 {
2787 MetaWindow *window = meta_window_actor_get_meta_window (self);
2788 float refresh_rate;
2789 int refresh_interval;
2790
2791 refresh_rate = window->monitor->refresh_rate;
2792 /* 0.0 is a flag for not known, but sanity-check against other odd numbers */
2793 if (refresh_rate >= 1.0)
2794 refresh_interval = (int) (0.5 + 1000000 / refresh_rate);
2795 else
2796 refresh_interval = 0;
2797
2798 do_send_frame_timings (self, frame, refresh_interval, presentation_time);
2799 }
2800
2801 void
meta_window_actor_frame_complete(MetaWindowActor * self,ClutterFrameInfo * frame_info,gint64 presentation_time)2802 meta_window_actor_frame_complete (MetaWindowActor *self,
2803 ClutterFrameInfo *frame_info,
2804 gint64 presentation_time)
2805 {
2806 MetaWindowActorPrivate *priv = self->priv;
2807 GList *l;
2808
2809 if (meta_window_actor_is_destroyed (self))
2810 return;
2811
2812 for (l = priv->frames; l;)
2813 {
2814 GList *l_next = l->next;
2815 FrameData *frame = l->data;
2816 gint64 frame_counter = frame_info->frame_counter;
2817
2818 if (frame->frame_counter != -1 && frame->frame_counter <= frame_counter)
2819 {
2820 if (G_UNLIKELY (frame->frame_drawn_time == 0))
2821 g_warning ("%s: Frame has assigned frame counter but no frame drawn time",
2822 priv->window->desc);
2823 if (G_UNLIKELY (frame->frame_counter < frame_counter))
2824 g_warning ("%s: frame_complete callback never occurred for frame %" G_GINT64_FORMAT,
2825 priv->window->desc, frame->frame_counter);
2826
2827 priv->frames = g_list_delete_link (priv->frames, l);
2828 send_frame_timings (self, frame, frame_info, presentation_time);
2829 frame_data_free (frame);
2830 }
2831
2832 l = l_next;
2833 }
2834 }
2835
2836 LOCAL_SYMBOL void
meta_window_actor_invalidate_shadow(MetaWindowActor * self)2837 meta_window_actor_invalidate_shadow (MetaWindowActor *self)
2838 {
2839 MetaWindowActorPrivate *priv = self->priv;
2840
2841 priv->recompute_focused_shadow = TRUE;
2842 priv->recompute_unfocused_shadow = TRUE;
2843
2844 if (is_frozen (self))
2845 return;
2846
2847 clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
2848 }
2849
2850 LOCAL_SYMBOL void
meta_window_actor_update_opacity(MetaWindowActor * self)2851 meta_window_actor_update_opacity (MetaWindowActor *self)
2852 {
2853 MetaWindowActorPrivate *priv = self->priv;
2854 MetaDisplay *display = meta_screen_get_display (priv->screen);
2855 MetaCompositor *compositor = meta_display_get_compositor (display);
2856 Window xwin = meta_window_get_xwindow (priv->window);
2857 gulong value;
2858 guint8 opacity;
2859
2860 if (meta_prop_get_cardinal (display, xwin,
2861 compositor->atom_net_wm_window_opacity,
2862 &value))
2863 {
2864 opacity = (guint8)((gfloat)value * 255.0 / ((gfloat)0xffffffff));
2865 }
2866 else
2867 opacity = 255;
2868
2869 self->priv->opacity = opacity;
2870 clutter_actor_set_opacity (self->priv->actor, opacity);
2871 }
2872
2873 void
meta_window_actor_set_updates_frozen(MetaWindowActor * self,gboolean updates_frozen)2874 meta_window_actor_set_updates_frozen (MetaWindowActor *self,
2875 gboolean updates_frozen)
2876 {
2877 MetaWindowActorPrivate *priv = self->priv;
2878
2879 updates_frozen = updates_frozen != FALSE;
2880
2881 if (priv->updates_frozen != updates_frozen)
2882 {
2883 priv->updates_frozen = updates_frozen;
2884 if (updates_frozen)
2885 meta_window_actor_freeze (self);
2886 else
2887 meta_window_actor_thaw (self);
2888 }
2889 }
2890