2018-12-21 15:35:18 -05:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2018 Endless, Inc.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License as
|
|
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
|
|
* License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
|
|
* 02111-1307, USA.
|
|
|
|
*
|
|
|
|
* Written by:
|
|
|
|
* Georges Basile Stavracas Neto <gbsneto@gnome.org>
|
|
|
|
*/
|
|
|
|
|
2019-06-27 10:18:47 -04:00
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include "compositor/meta-window-actor-x11.h"
|
|
|
|
|
2018-12-21 15:51:02 -05:00
|
|
|
#include "backends/meta-logical-monitor.h"
|
clutter: Paint views with individual frame clocks
Replace the default master clock with multiple frame clocks, each
driving its own stage view. As each stage view represents one CRTC, this
means we draw each CRTC with its own designated frame clock,
disconnected from all the others.
For example this means we when using the native backend will never need
to wait for one monitor to vsync before painting another, so e.g. having
a 144 Hz monitor next to a 60 Hz monitor, things including both Wayland
and X11 applications and shell UI will be able to render at the
corresponding monitor refresh rate.
This also changes a warning about missed frames when sending
_NETWM_FRAME_TIMINGS messages to a debug log entry, as it's expected
that we'll start missing frames e.g. when a X11 window (via Xwayland) is
exclusively within a stage view that was not painted, while another one
was, still increasing the global frame clock.
Addititonally, this also requires the X11 window actor to schedule
timeouts for _NET_WM_FRAME_DRAWN/_NET_WM_FRAME_TIMINGS event emitting,
if the actor wasn't on any stage views, as now we'll only get the frame
callbacks on actors when they actually were painted, while in the past,
we'd invoke that vfunc when anything was painted.
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/903
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/3
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1285
2020-05-29 18:27:56 -04:00
|
|
|
#include "clutter/clutter-frame-clock.h"
|
2018-12-21 15:51:02 -05:00
|
|
|
#include "compositor/compositor-private.h"
|
2019-08-17 04:00:46 -04:00
|
|
|
#include "compositor/meta-cullable.h"
|
|
|
|
#include "compositor/meta-shaped-texture-private.h"
|
2018-12-21 15:51:02 -05:00
|
|
|
#include "compositor/meta-surface-actor.h"
|
2019-08-17 04:00:46 -04:00
|
|
|
#include "compositor/meta-surface-actor-x11.h"
|
|
|
|
#include "compositor/region-utils.h"
|
|
|
|
#include "core/frame.h"
|
2018-12-21 15:51:02 -05:00
|
|
|
#include "core/window-private.h"
|
|
|
|
#include "meta/compositor.h"
|
2019-08-17 04:00:46 -04:00
|
|
|
#include "meta/meta-enum-types.h"
|
|
|
|
#include "meta/meta-shadow-factory.h"
|
2018-12-21 15:35:18 -05:00
|
|
|
#include "meta/meta-window-actor.h"
|
2018-12-21 15:51:02 -05:00
|
|
|
#include "meta/meta-x11-errors.h"
|
|
|
|
#include "meta/window.h"
|
2019-08-20 15:46:50 -04:00
|
|
|
#include "x11/window-x11.h"
|
2022-09-14 18:30:23 -04:00
|
|
|
#include "x11/meta-sync-counter.h"
|
2018-12-21 15:51:02 -05:00
|
|
|
#include "x11/meta-x11-display-private.h"
|
2019-12-02 08:01:30 -05:00
|
|
|
#include "x11/window-x11.h"
|
2018-12-21 15:35:18 -05:00
|
|
|
|
2019-08-17 04:00:46 -04:00
|
|
|
enum
|
|
|
|
{
|
|
|
|
PROP_SHADOW_MODE = 1,
|
|
|
|
PROP_SHADOW_CLASS
|
|
|
|
};
|
|
|
|
|
2018-12-21 15:35:18 -05:00
|
|
|
struct _MetaWindowActorX11
|
|
|
|
{
|
|
|
|
MetaWindowActor parent;
|
2018-12-21 15:51:02 -05:00
|
|
|
|
|
|
|
guint send_frame_messages_timer;
|
clutter: Paint views with individual frame clocks
Replace the default master clock with multiple frame clocks, each
driving its own stage view. As each stage view represents one CRTC, this
means we draw each CRTC with its own designated frame clock,
disconnected from all the others.
For example this means we when using the native backend will never need
to wait for one monitor to vsync before painting another, so e.g. having
a 144 Hz monitor next to a 60 Hz monitor, things including both Wayland
and X11 applications and shell UI will be able to render at the
corresponding monitor refresh rate.
This also changes a warning about missed frames when sending
_NETWM_FRAME_TIMINGS messages to a debug log entry, as it's expected
that we'll start missing frames e.g. when a X11 window (via Xwayland) is
exclusively within a stage view that was not painted, while another one
was, still increasing the global frame clock.
Addititonally, this also requires the X11 window actor to schedule
timeouts for _NET_WM_FRAME_DRAWN/_NET_WM_FRAME_TIMINGS event emitting,
if the actor wasn't on any stage views, as now we'll only get the frame
callbacks on actors when they actually were painted, while in the past,
we'd invoke that vfunc when anything was painted.
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/903
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/3
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1285
2020-05-29 18:27:56 -04:00
|
|
|
gboolean pending_schedule_update_now;
|
2018-12-21 15:51:02 -05:00
|
|
|
|
2019-08-16 15:09:18 -04:00
|
|
|
gulong repaint_scheduled_id;
|
2019-08-17 04:00:46 -04:00
|
|
|
gulong size_changed_id;
|
2018-12-21 15:51:02 -05:00
|
|
|
|
|
|
|
gboolean repaint_scheduled;
|
2019-08-17 04:00:46 -04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* MetaShadowFactory only caches shadows that are actually in use;
|
|
|
|
* to avoid unnecessary recomputation we do two things: 1) we store
|
|
|
|
* both a focused and unfocused shadow for the window. If the window
|
|
|
|
* doesn't have different focused and unfocused shadow parameters,
|
|
|
|
* these will be the same. 2) when the shadow potentially changes we
|
|
|
|
* don't immediately unreference the old shadow, we just flag it as
|
|
|
|
* dirty and recompute it when we next need it (recompute_focused_shadow,
|
|
|
|
* recompute_unfocused_shadow.) Because of our extraction of
|
|
|
|
* size-invariant window shape, we'll often find that the new shadow
|
|
|
|
* is the same as the old shadow.
|
|
|
|
*/
|
|
|
|
MetaShadow *focused_shadow;
|
|
|
|
MetaShadow *unfocused_shadow;
|
|
|
|
|
|
|
|
/* A region that matches the shape of the window, including frame bounds */
|
|
|
|
cairo_region_t *shape_region;
|
|
|
|
/* The region we should clip to when painting the shadow */
|
|
|
|
cairo_region_t *shadow_clip;
|
2020-04-24 06:19:02 -04:00
|
|
|
/* The frame region */
|
|
|
|
cairo_region_t *frame_bounds;
|
2019-08-17 04:00:46 -04:00
|
|
|
|
|
|
|
/* Extracted size-invariant shape used for shadows */
|
|
|
|
MetaWindowShape *shadow_shape;
|
|
|
|
char *shadow_class;
|
|
|
|
|
|
|
|
MetaShadowFactory *shadow_factory;
|
|
|
|
gulong shadow_factory_changed_handler_id;
|
|
|
|
|
|
|
|
MetaShadowMode shadow_mode;
|
|
|
|
|
|
|
|
gboolean needs_reshape;
|
|
|
|
gboolean recompute_focused_shadow;
|
|
|
|
gboolean recompute_unfocused_shadow;
|
2019-11-19 08:30:44 -05:00
|
|
|
gboolean is_frozen;
|
2018-12-21 15:35:18 -05:00
|
|
|
};
|
|
|
|
|
2019-08-17 04:00:46 -04:00
|
|
|
static void cullable_iface_init (MetaCullableInterface *iface);
|
|
|
|
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (MetaWindowActorX11, meta_window_actor_x11, META_TYPE_WINDOW_ACTOR,
|
|
|
|
G_IMPLEMENT_INTERFACE (META_TYPE_CULLABLE, cullable_iface_init))
|
2018-12-21 15:35:18 -05:00
|
|
|
|
2018-12-21 15:51:02 -05:00
|
|
|
static void
|
|
|
|
surface_repaint_scheduled (MetaSurfaceActor *actor,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (user_data);
|
|
|
|
|
|
|
|
actor_x11->repaint_scheduled = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
remove_frame_messages_timer (MetaWindowActorX11 *actor_x11)
|
|
|
|
{
|
|
|
|
g_assert (actor_x11->send_frame_messages_timer != 0);
|
|
|
|
|
2019-11-21 18:25:30 -05:00
|
|
|
g_clear_handle_id (&actor_x11->send_frame_messages_timer, g_source_remove);
|
2018-12-21 15:51:02 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
send_frame_messages_timeout (gpointer data)
|
|
|
|
{
|
|
|
|
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (data);
|
2022-09-14 18:30:23 -04:00
|
|
|
MetaWindow *window =
|
|
|
|
meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
|
|
|
|
MetaSyncCounter *sync_counter;
|
2018-12-21 15:51:02 -05:00
|
|
|
|
2022-09-14 18:30:23 -04:00
|
|
|
sync_counter = meta_window_x11_get_sync_counter (window);
|
|
|
|
meta_sync_counter_finish_incomplete (sync_counter);
|
2022-09-09 09:54:28 -04:00
|
|
|
|
|
|
|
if (window->frame)
|
|
|
|
{
|
|
|
|
sync_counter = meta_frame_get_sync_counter (window->frame);
|
|
|
|
meta_sync_counter_finish_incomplete (sync_counter);
|
|
|
|
}
|
|
|
|
|
2018-12-21 15:51:02 -05:00
|
|
|
actor_x11->send_frame_messages_timer = 0;
|
|
|
|
|
|
|
|
return G_SOURCE_REMOVE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
queue_send_frame_messages_timeout (MetaWindowActorX11 *actor_x11)
|
|
|
|
{
|
|
|
|
MetaWindow *window =
|
|
|
|
meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
|
|
|
|
MetaDisplay *display = meta_window_get_display (window);
|
|
|
|
MetaLogicalMonitor *logical_monitor;
|
2022-09-14 18:30:23 -04:00
|
|
|
MetaSyncCounter *sync_counter;
|
compositor: Make sure _NET_WM_FRAME_DRAWN timestamp has the right scope
The timestamp sent with _NET_WM_FRAME_DRAWN should be in "high
resolution X server timestamps", meaning they should have the same scope
as the built in X11 32 bit unsigned integer timestamps, i.e. overflow at
the same time.
This was not done correctly when mutter had determined the X server used
the monotonic clock, where it'd just forward the monotonic clock,
confusing any client using _NET_WM_FRAME_DRAWN and friends.
Fix this by 1) splitting the timestamp conversiot into an X11 case and a
display server case, where the display server case simply clamps the
monotonic clock, as it is assumed Xwayland is always usign the monotonic
clock, and 2) if we're a X11 compositing manager, if the X server is
using the monotonic clock, apply the same semantics as the display
server case and always just clamp, or if not, calculate the offset every
10 seconds, and offset the monotonic clock timestamp with the calculated
X server timestamp offset.
This fixes an issue that would occur if mutter (or rather GNOME Shell)
would have been started before a X11 timestamp overflow, after the
overflow happened. In this case, GTK3 clients would get unclamped
timestamps, and get very confused, resulting in frames queued several
weeks into the future.
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1494
2020-07-08 10:53:14 -04:00
|
|
|
int64_t now_us;
|
2018-12-21 15:51:02 -05:00
|
|
|
int64_t current_time;
|
|
|
|
float refresh_rate;
|
|
|
|
int interval, offset;
|
|
|
|
|
|
|
|
if (actor_x11->send_frame_messages_timer != 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
logical_monitor = meta_window_get_main_logical_monitor (window);
|
|
|
|
if (logical_monitor)
|
|
|
|
{
|
|
|
|
GList *monitors = meta_logical_monitor_get_monitors (logical_monitor);
|
|
|
|
MetaMonitor *monitor;
|
|
|
|
MetaMonitorMode *mode;
|
|
|
|
|
|
|
|
monitor = g_list_first (monitors)->data;
|
|
|
|
mode = meta_monitor_get_current_mode (monitor);
|
|
|
|
|
|
|
|
refresh_rate = meta_monitor_mode_get_refresh_rate (mode);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
refresh_rate = 60.0f;
|
|
|
|
}
|
|
|
|
|
compositor: Make sure _NET_WM_FRAME_DRAWN timestamp has the right scope
The timestamp sent with _NET_WM_FRAME_DRAWN should be in "high
resolution X server timestamps", meaning they should have the same scope
as the built in X11 32 bit unsigned integer timestamps, i.e. overflow at
the same time.
This was not done correctly when mutter had determined the X server used
the monotonic clock, where it'd just forward the monotonic clock,
confusing any client using _NET_WM_FRAME_DRAWN and friends.
Fix this by 1) splitting the timestamp conversiot into an X11 case and a
display server case, where the display server case simply clamps the
monotonic clock, as it is assumed Xwayland is always usign the monotonic
clock, and 2) if we're a X11 compositing manager, if the X server is
using the monotonic clock, apply the same semantics as the display
server case and always just clamp, or if not, calculate the offset every
10 seconds, and offset the monotonic clock timestamp with the calculated
X server timestamp offset.
This fixes an issue that would occur if mutter (or rather GNOME Shell)
would have been started before a X11 timestamp overflow, after the
overflow happened. In this case, GTK3 clients would get unclamped
timestamps, and get very confused, resulting in frames queued several
weeks into the future.
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1494
2020-07-08 10:53:14 -04:00
|
|
|
now_us = g_get_monotonic_time ();
|
2018-12-21 15:51:02 -05:00
|
|
|
current_time =
|
compositor: Make sure _NET_WM_FRAME_DRAWN timestamp has the right scope
The timestamp sent with _NET_WM_FRAME_DRAWN should be in "high
resolution X server timestamps", meaning they should have the same scope
as the built in X11 32 bit unsigned integer timestamps, i.e. overflow at
the same time.
This was not done correctly when mutter had determined the X server used
the monotonic clock, where it'd just forward the monotonic clock,
confusing any client using _NET_WM_FRAME_DRAWN and friends.
Fix this by 1) splitting the timestamp conversiot into an X11 case and a
display server case, where the display server case simply clamps the
monotonic clock, as it is assumed Xwayland is always usign the monotonic
clock, and 2) if we're a X11 compositing manager, if the X server is
using the monotonic clock, apply the same semantics as the display
server case and always just clamp, or if not, calculate the offset every
10 seconds, and offset the monotonic clock timestamp with the calculated
X server timestamp offset.
This fixes an issue that would occur if mutter (or rather GNOME Shell)
would have been started before a X11 timestamp overflow, after the
overflow happened. In this case, GTK3 clients would get unclamped
timestamps, and get very confused, resulting in frames queued several
weeks into the future.
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1494
2020-07-08 10:53:14 -04:00
|
|
|
meta_compositor_monotonic_to_high_res_xserver_time (display->compositor,
|
|
|
|
now_us);
|
2018-12-21 15:51:02 -05:00
|
|
|
interval = (int) (1000000 / refresh_rate) * 6;
|
2022-09-14 18:30:23 -04:00
|
|
|
sync_counter = meta_window_x11_get_sync_counter (window);
|
|
|
|
offset = MAX (0, sync_counter->frame_drawn_time + interval - current_time) / 1000;
|
2018-12-21 15:51:02 -05:00
|
|
|
|
|
|
|
/* The clutter master clock source has already been added with META_PRIORITY_REDRAW,
|
|
|
|
* so the timer will run *after* the clutter frame handling, if a frame is ready
|
|
|
|
* to be drawn when the timer expires.
|
|
|
|
*/
|
|
|
|
actor_x11->send_frame_messages_timer =
|
|
|
|
g_timeout_add_full (META_PRIORITY_REDRAW, offset,
|
|
|
|
send_frame_messages_timeout,
|
|
|
|
actor_x11, NULL);
|
|
|
|
g_source_set_name_by_id (actor_x11->send_frame_messages_timer,
|
|
|
|
"[mutter] send_frame_messages_timeout");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
assign_frame_counter_to_frames (MetaWindowActorX11 *actor_x11)
|
|
|
|
{
|
|
|
|
MetaWindow *window =
|
|
|
|
meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
|
|
|
|
MetaCompositor *compositor = window->display->compositor;
|
2019-08-14 13:04:41 -04:00
|
|
|
ClutterStage *stage = meta_compositor_get_stage (compositor);
|
2022-09-14 18:30:23 -04:00
|
|
|
MetaSyncCounter *sync_counter;
|
2018-12-21 15:51:02 -05:00
|
|
|
|
|
|
|
/* If the window is obscured, then we're expecting to deal with sending
|
|
|
|
* frame messages in a timeout, rather than in this paint cycle.
|
|
|
|
*/
|
|
|
|
if (actor_x11->send_frame_messages_timer != 0)
|
|
|
|
return;
|
|
|
|
|
2022-09-14 18:30:23 -04:00
|
|
|
sync_counter = meta_window_x11_get_sync_counter (window);
|
|
|
|
meta_sync_counter_assign_counter_to_frames (sync_counter,
|
|
|
|
clutter_stage_get_frame_counter (stage));
|
2022-09-09 09:54:28 -04:00
|
|
|
|
|
|
|
if (window->frame)
|
|
|
|
{
|
|
|
|
sync_counter = meta_frame_get_sync_counter (window->frame);
|
|
|
|
meta_sync_counter_assign_counter_to_frames (sync_counter,
|
|
|
|
clutter_stage_get_frame_counter (stage));
|
|
|
|
}
|
2018-12-21 15:51:02 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
meta_window_actor_x11_frame_complete (MetaWindowActor *actor,
|
|
|
|
ClutterFrameInfo *frame_info,
|
|
|
|
int64_t presentation_time)
|
|
|
|
{
|
2022-09-14 18:30:23 -04:00
|
|
|
MetaWindow *window = meta_window_actor_get_meta_window (actor);
|
|
|
|
MetaSyncCounter *sync_counter;
|
2018-12-21 15:51:02 -05:00
|
|
|
|
|
|
|
if (meta_window_actor_is_destroyed (actor))
|
|
|
|
return;
|
|
|
|
|
2022-09-14 18:30:23 -04:00
|
|
|
sync_counter = meta_window_x11_get_sync_counter (window);
|
|
|
|
meta_sync_counter_complete_frame (sync_counter,
|
|
|
|
frame_info,
|
|
|
|
presentation_time);
|
2022-09-09 09:54:28 -04:00
|
|
|
|
|
|
|
if (window->frame)
|
|
|
|
{
|
|
|
|
sync_counter = meta_frame_get_sync_counter (window->frame);
|
|
|
|
meta_sync_counter_complete_frame (sync_counter,
|
|
|
|
frame_info,
|
|
|
|
presentation_time);
|
|
|
|
}
|
2018-12-21 15:51:02 -05:00
|
|
|
}
|
|
|
|
|
2022-05-11 16:32:33 -04:00
|
|
|
static MetaSurfaceActor *
|
|
|
|
meta_window_actor_x11_get_scanout_candidate (MetaWindowActor *actor)
|
|
|
|
{
|
|
|
|
MetaSurfaceActor *surface_actor;
|
|
|
|
|
|
|
|
surface_actor = meta_window_actor_get_surface (actor);
|
|
|
|
|
|
|
|
if (!surface_actor)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (CLUTTER_ACTOR (surface_actor) !=
|
|
|
|
clutter_actor_get_last_child (CLUTTER_ACTOR (actor)))
|
|
|
|
return NULL;
|
|
|
|
|
2022-05-11 16:41:29 -04:00
|
|
|
if (!meta_window_actor_is_opaque (actor))
|
|
|
|
return NULL;
|
|
|
|
|
2022-05-11 16:32:33 -04:00
|
|
|
return surface_actor;
|
|
|
|
}
|
|
|
|
|
2019-08-17 04:00:46 -04:00
|
|
|
static void
|
|
|
|
surface_size_changed (MetaSurfaceActor *actor,
|
|
|
|
gpointer user_data)
|
|
|
|
{
|
|
|
|
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (user_data);
|
|
|
|
|
|
|
|
meta_window_actor_x11_update_shape (actor_x11);
|
|
|
|
}
|
|
|
|
|
2018-12-21 15:51:02 -05:00
|
|
|
static void
|
2019-06-27 13:33:24 -04:00
|
|
|
meta_window_actor_x11_assign_surface_actor (MetaWindowActor *actor,
|
|
|
|
MetaSurfaceActor *surface_actor)
|
2018-12-21 15:51:02 -05:00
|
|
|
{
|
|
|
|
MetaWindowActorClass *parent_class =
|
|
|
|
META_WINDOW_ACTOR_CLASS (meta_window_actor_x11_parent_class);
|
|
|
|
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor);
|
2019-08-17 04:00:46 -04:00
|
|
|
MetaSurfaceActor *prev_surface_actor;
|
|
|
|
|
|
|
|
prev_surface_actor = meta_window_actor_get_surface (actor);
|
|
|
|
if (prev_surface_actor)
|
|
|
|
{
|
|
|
|
g_warn_if_fail (meta_is_wayland_compositor ());
|
|
|
|
|
|
|
|
g_clear_signal_handler (&actor_x11->size_changed_id, prev_surface_actor);
|
|
|
|
clutter_actor_remove_child (CLUTTER_ACTOR (actor),
|
|
|
|
CLUTTER_ACTOR (prev_surface_actor));
|
|
|
|
}
|
2018-12-21 15:51:02 -05:00
|
|
|
|
2019-06-27 13:33:24 -04:00
|
|
|
parent_class->assign_surface_actor (actor, surface_actor);
|
2018-12-21 15:51:02 -05:00
|
|
|
|
2019-06-30 09:18:46 -04:00
|
|
|
clutter_actor_add_child (CLUTTER_ACTOR (actor),
|
|
|
|
CLUTTER_ACTOR (surface_actor));
|
|
|
|
|
2019-08-17 04:00:46 -04:00
|
|
|
meta_window_actor_x11_update_shape (actor_x11);
|
|
|
|
|
|
|
|
actor_x11->size_changed_id =
|
|
|
|
g_signal_connect (surface_actor, "size-changed",
|
|
|
|
G_CALLBACK (surface_size_changed),
|
|
|
|
actor_x11);
|
2019-06-27 13:33:24 -04:00
|
|
|
actor_x11->repaint_scheduled_id =
|
|
|
|
g_signal_connect (surface_actor, "repaint-scheduled",
|
|
|
|
G_CALLBACK (surface_repaint_scheduled),
|
|
|
|
actor_x11);
|
2018-12-21 15:51:02 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
meta_window_actor_x11_queue_frame_drawn (MetaWindowActor *actor,
|
|
|
|
gboolean skip_sync_delay)
|
|
|
|
{
|
|
|
|
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor);
|
|
|
|
|
|
|
|
if (meta_window_actor_is_destroyed (actor))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (skip_sync_delay)
|
|
|
|
{
|
window-actor/x11: Don't cache the frame-drawn frame clock
We fetch a frame clock that we schedule update on when queuing
_NET_WM_FRAME_DRAWN events. In some situations this frame clock is the
one from the stage, and if there are multiple hotplugs in a row, we
failed to update it as there were no stage views changes on the window
actor itself. As an actor updates the stage views list on layout, When a
queue_frame_drawn() call was done (typically from an X11 event) after a
second hotplug, it'd attempt to schedule an update on the frame clock
from the previous hotplug, as it didn't get notified about any
stage-views changes since for itself there was none.
Fix this by not caching the frame clock at all and just fetch it every
time.
In the majority of cases, this fetching means iterating over a very
short list (most often a single entry, rarely more), so it's very
unlikely to be of any relevance. The only situations where it might be a
heavier operation is the short time between a hotplug and a layout, as
it will attempt to traverse up to the stage to find a clock, but that's
likely only a few levels, so even that is unlikely to be an issue.
Closes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/4486
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1947>
2021-07-30 05:35:10 -04:00
|
|
|
ClutterFrameClock *frame_clock;
|
|
|
|
|
|
|
|
frame_clock = clutter_actor_pick_frame_clock (CLUTTER_ACTOR (actor),
|
|
|
|
NULL);
|
|
|
|
if (frame_clock)
|
|
|
|
clutter_frame_clock_schedule_update_now (frame_clock);
|
clutter: Paint views with individual frame clocks
Replace the default master clock with multiple frame clocks, each
driving its own stage view. As each stage view represents one CRTC, this
means we draw each CRTC with its own designated frame clock,
disconnected from all the others.
For example this means we when using the native backend will never need
to wait for one monitor to vsync before painting another, so e.g. having
a 144 Hz monitor next to a 60 Hz monitor, things including both Wayland
and X11 applications and shell UI will be able to render at the
corresponding monitor refresh rate.
This also changes a warning about missed frames when sending
_NETWM_FRAME_TIMINGS messages to a debug log entry, as it's expected
that we'll start missing frames e.g. when a X11 window (via Xwayland) is
exclusively within a stage view that was not painted, while another one
was, still increasing the global frame clock.
Addititonally, this also requires the X11 window actor to schedule
timeouts for _NET_WM_FRAME_DRAWN/_NET_WM_FRAME_TIMINGS event emitting,
if the actor wasn't on any stage views, as now we'll only get the frame
callbacks on actors when they actually were painted, while in the past,
we'd invoke that vfunc when anything was painted.
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/903
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/3
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1285
2020-05-29 18:27:56 -04:00
|
|
|
else
|
|
|
|
actor_x11->pending_schedule_update_now = TRUE;
|
2018-12-21 15:51:02 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!actor_x11->repaint_scheduled)
|
|
|
|
{
|
|
|
|
MetaSurfaceActor *surface;
|
|
|
|
gboolean is_obscured;
|
|
|
|
|
|
|
|
surface = meta_window_actor_get_surface (META_WINDOW_ACTOR (actor_x11));
|
|
|
|
|
|
|
|
if (surface)
|
|
|
|
is_obscured = meta_surface_actor_is_obscured (surface);
|
|
|
|
else
|
|
|
|
is_obscured = FALSE;
|
|
|
|
|
|
|
|
/* A frame was marked by the client without actually doing any
|
|
|
|
* damage or any unobscured, or while we had the window frozen
|
|
|
|
* (e.g. during an interactive resize.) We need to make sure that the
|
2020-05-29 18:02:42 -04:00
|
|
|
* before_paint/after_paint functions get called, enabling us to
|
2021-03-05 03:55:34 -05:00
|
|
|
* send a _NET_WM_FRAME_DRAWN. We need to do full damage to ensure that
|
|
|
|
* the window is actually repainted, otherwise any subregion we would pass
|
|
|
|
* might end up being either outside of any stage view, or be occluded by
|
|
|
|
* something else, which could potentially result in no frame being drawn
|
|
|
|
* after all. If the window is completely obscured, or completely off
|
|
|
|
* screen we fire off the send_frame_messages timeout.
|
2018-12-21 15:51:02 -05:00
|
|
|
*/
|
clutter: Paint views with individual frame clocks
Replace the default master clock with multiple frame clocks, each
driving its own stage view. As each stage view represents one CRTC, this
means we draw each CRTC with its own designated frame clock,
disconnected from all the others.
For example this means we when using the native backend will never need
to wait for one monitor to vsync before painting another, so e.g. having
a 144 Hz monitor next to a 60 Hz monitor, things including both Wayland
and X11 applications and shell UI will be able to render at the
corresponding monitor refresh rate.
This also changes a warning about missed frames when sending
_NETWM_FRAME_TIMINGS messages to a debug log entry, as it's expected
that we'll start missing frames e.g. when a X11 window (via Xwayland) is
exclusively within a stage view that was not painted, while another one
was, still increasing the global frame clock.
Addititonally, this also requires the X11 window actor to schedule
timeouts for _NET_WM_FRAME_DRAWN/_NET_WM_FRAME_TIMINGS event emitting,
if the actor wasn't on any stage views, as now we'll only get the frame
callbacks on actors when they actually were painted, while in the past,
we'd invoke that vfunc when anything was painted.
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/903
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/3
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1285
2020-05-29 18:27:56 -04:00
|
|
|
if (is_obscured ||
|
|
|
|
!clutter_actor_peek_stage_views (CLUTTER_ACTOR (actor)))
|
2018-12-21 15:51:02 -05:00
|
|
|
{
|
|
|
|
queue_send_frame_messages_timeout (actor_x11);
|
|
|
|
}
|
|
|
|
else if (surface)
|
|
|
|
{
|
2021-03-05 03:55:34 -05:00
|
|
|
clutter_actor_queue_redraw (CLUTTER_ACTOR (surface));
|
2018-12-21 15:51:02 -05:00
|
|
|
actor_x11->repaint_scheduled = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-17 04:00:46 -04:00
|
|
|
static gboolean
|
|
|
|
has_shadow (MetaWindowActorX11 *actor_x11)
|
|
|
|
{
|
|
|
|
MetaWindow *window =
|
|
|
|
meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
|
|
|
|
|
|
|
|
if (actor_x11->shadow_mode == META_SHADOW_MODE_FORCED_OFF)
|
|
|
|
return FALSE;
|
|
|
|
if (actor_x11->shadow_mode == META_SHADOW_MODE_FORCED_ON)
|
|
|
|
return TRUE;
|
|
|
|
|
2020-08-26 05:49:50 -04:00
|
|
|
/* Leaving out shadows for maximized and fullscreen windows is an efficiency
|
2019-08-17 04:00:46 -04:00
|
|
|
* win and also prevents the unsightly effect of the shadow of maximized
|
|
|
|
* window appearing on an adjacent window */
|
|
|
|
if ((meta_window_get_maximized (window) == META_MAXIMIZE_BOTH) ||
|
|
|
|
meta_window_is_fullscreen (window))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we have two snap-tiled windows, we don't want the shadow to obstruct
|
|
|
|
* the other window.
|
|
|
|
*/
|
|
|
|
if (meta_window_get_tile_match (window))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
/*
|
2022-09-12 06:27:03 -04:00
|
|
|
* Let the frames client put a shadow around frames - This should override
|
2019-08-17 04:00:46 -04:00
|
|
|
* the restriction about not putting a shadow around ARGB windows.
|
|
|
|
*/
|
|
|
|
if (meta_window_get_frame (window))
|
2022-09-12 06:27:03 -04:00
|
|
|
return FALSE;
|
2019-08-17 04:00:46 -04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Do not add shadows to non-opaque (ARGB32) windows, as we can't easily
|
|
|
|
* generate shadows for them.
|
|
|
|
*/
|
|
|
|
if (!meta_window_actor_is_opaque (META_WINDOW_ACTOR (actor_x11)))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If a window specifies that it has custom frame extents, that likely
|
|
|
|
* means that it is drawing a shadow itself. Don't draw our own.
|
|
|
|
*/
|
|
|
|
if (window->has_custom_frame_extents)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Generate shadows for all other windows.
|
|
|
|
*/
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
meta_window_actor_x11_should_unredirect (MetaWindowActorX11 *actor_x11)
|
|
|
|
{
|
2019-08-20 15:46:50 -04:00
|
|
|
MetaWindow *window =
|
|
|
|
meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
|
|
|
|
MetaWindowX11 *window_x11 = META_WINDOW_X11 (window);
|
2019-08-17 04:00:46 -04:00
|
|
|
MetaSurfaceActor *surface;
|
|
|
|
MetaSurfaceActorX11 *surface_x11;
|
|
|
|
|
|
|
|
if (meta_window_actor_is_destroyed (META_WINDOW_ACTOR (actor_x11)))
|
|
|
|
return FALSE;
|
|
|
|
|
2019-08-20 15:46:50 -04:00
|
|
|
if (!meta_window_x11_can_unredirect (window_x11))
|
|
|
|
return FALSE;
|
|
|
|
|
2019-08-17 04:00:46 -04:00
|
|
|
surface = meta_window_actor_get_surface (META_WINDOW_ACTOR (actor_x11));
|
|
|
|
if (!surface)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (!META_IS_SURFACE_ACTOR_X11 (surface))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
surface_x11 = META_SURFACE_ACTOR_X11 (surface);
|
|
|
|
return meta_surface_actor_x11_should_unredirect (surface_x11);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
meta_window_actor_x11_set_unredirected (MetaWindowActorX11 *actor_x11,
|
|
|
|
gboolean unredirected)
|
|
|
|
{
|
|
|
|
MetaSurfaceActor *surface;
|
|
|
|
MetaSurfaceActorX11 *surface_x11;
|
|
|
|
|
|
|
|
surface = meta_window_actor_get_surface (META_WINDOW_ACTOR (actor_x11));
|
|
|
|
g_assert (surface);
|
|
|
|
|
|
|
|
g_return_if_fail (META_IS_SURFACE_ACTOR_X11 (surface));
|
|
|
|
|
|
|
|
surface_x11 = META_SURFACE_ACTOR_X11 (surface);
|
|
|
|
meta_surface_actor_x11_set_unredirected (surface_x11, unredirected);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
get_shadow_class (MetaWindowActorX11 *actor_x11)
|
|
|
|
{
|
|
|
|
if (actor_x11->shadow_class)
|
|
|
|
{
|
|
|
|
return actor_x11->shadow_class;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
MetaWindow *window =
|
|
|
|
meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
|
|
|
|
MetaWindowType window_type;
|
|
|
|
|
|
|
|
window_type = meta_window_get_window_type (window);
|
|
|
|
switch (window_type)
|
|
|
|
{
|
|
|
|
case META_WINDOW_DROPDOWN_MENU:
|
|
|
|
case META_WINDOW_COMBO:
|
|
|
|
return "dropdown-menu";
|
|
|
|
case META_WINDOW_POPUP_MENU:
|
|
|
|
return "popup-menu";
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
MetaFrameType frame_type;
|
|
|
|
|
|
|
|
frame_type = meta_window_get_frame_type (window);
|
|
|
|
return meta_frame_type_to_string (frame_type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
get_shadow_params (MetaWindowActorX11 *actor_x11,
|
|
|
|
gboolean appears_focused,
|
|
|
|
MetaShadowParams *params)
|
|
|
|
{
|
|
|
|
const char *shadow_class = get_shadow_class (actor_x11);
|
|
|
|
|
|
|
|
meta_shadow_factory_get_params (actor_x11->shadow_factory,
|
|
|
|
shadow_class, appears_focused,
|
|
|
|
params);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
get_shape_bounds (MetaWindowActorX11 *actor_x11,
|
|
|
|
cairo_rectangle_int_t *bounds)
|
|
|
|
{
|
|
|
|
cairo_region_get_extents (actor_x11->shape_region, bounds);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
get_shadow_bounds (MetaWindowActorX11 *actor_x11,
|
|
|
|
gboolean appears_focused,
|
|
|
|
cairo_rectangle_int_t *bounds)
|
|
|
|
{
|
|
|
|
MetaShadow *shadow;
|
|
|
|
cairo_rectangle_int_t shape_bounds;
|
|
|
|
MetaShadowParams params;
|
|
|
|
|
|
|
|
shadow = appears_focused ? actor_x11->focused_shadow
|
|
|
|
: actor_x11->unfocused_shadow;
|
|
|
|
|
|
|
|
get_shape_bounds (actor_x11, &shape_bounds);
|
|
|
|
get_shadow_params (actor_x11, appears_focused, ¶ms);
|
|
|
|
|
|
|
|
meta_shadow_get_bounds (shadow,
|
|
|
|
params.x_offset + shape_bounds.x,
|
|
|
|
params.y_offset + shape_bounds.y,
|
|
|
|
shape_bounds.width,
|
|
|
|
shape_bounds.height,
|
|
|
|
bounds);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If we have an ARGB32 window that we decorate with a frame, it's
|
|
|
|
* probably something like a translucent terminal - something where
|
|
|
|
* the alpha channel represents transparency rather than a shape. We
|
|
|
|
* don't want to show the shadow through the translucent areas since
|
|
|
|
* the shadow is wrong for translucent windows (it should be
|
|
|
|
* translucent itself and colored), and not only that, will /look/
|
|
|
|
* horribly wrong - a misplaced big black blob. As a hack, what we
|
|
|
|
* want to do is just draw the shadow as normal outside the frame, and
|
|
|
|
* inside the frame draw no shadow. This is also not even close to
|
|
|
|
* the right result, but looks OK. We also apply this approach to
|
|
|
|
* windows set to be partially translucent with _NET_WM_WINDOW_OPACITY.
|
|
|
|
*/
|
|
|
|
static gboolean
|
|
|
|
clip_shadow_under_window (MetaWindowActorX11 *actor_x11)
|
|
|
|
{
|
|
|
|
MetaWindow *window =
|
|
|
|
meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
|
|
|
|
|
|
|
|
if (window->frame)
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
return meta_window_actor_is_opaque (META_WINDOW_ACTOR (actor_x11));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* set_clip_region_beneath:
|
|
|
|
* @actor_x11: a #MetaWindowActorX11
|
|
|
|
* @clip_region: the region of the screen that isn't completely
|
|
|
|
* obscured beneath the main window texture.
|
|
|
|
*
|
|
|
|
* Provides a hint as to what areas need to be drawn *beneath*
|
|
|
|
* the main window texture. This is the relevant clip region
|
|
|
|
* when drawing the shadow, properly accounting for areas of the
|
|
|
|
* shadow hid by the window itself. This will be set before painting
|
|
|
|
* then unset afterwards.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
set_clip_region_beneath (MetaWindowActorX11 *actor_x11,
|
|
|
|
cairo_region_t *beneath_region)
|
|
|
|
{
|
|
|
|
MetaWindow *window;
|
|
|
|
gboolean appears_focused;
|
|
|
|
|
|
|
|
window = meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
|
|
|
|
appears_focused = meta_window_appears_focused (window);
|
|
|
|
if (appears_focused ? actor_x11->focused_shadow : actor_x11->unfocused_shadow)
|
|
|
|
{
|
|
|
|
g_clear_pointer (&actor_x11->shadow_clip, cairo_region_destroy);
|
|
|
|
|
|
|
|
if (beneath_region)
|
|
|
|
{
|
|
|
|
actor_x11->shadow_clip = cairo_region_copy (beneath_region);
|
|
|
|
|
|
|
|
if (clip_shadow_under_window (actor_x11))
|
|
|
|
{
|
2020-04-24 06:19:02 -04:00
|
|
|
if (actor_x11->frame_bounds)
|
|
|
|
cairo_region_subtract (actor_x11->shadow_clip, actor_x11->frame_bounds);
|
2019-08-17 04:00:46 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
actor_x11->shadow_clip = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
check_needs_shadow (MetaWindowActorX11 *actor_x11)
|
|
|
|
{
|
|
|
|
MetaWindow *window =
|
|
|
|
meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
|
|
|
|
MetaShadow *old_shadow = NULL;
|
|
|
|
MetaShadow **shadow_location;
|
|
|
|
gboolean recompute_shadow;
|
|
|
|
gboolean should_have_shadow;
|
|
|
|
gboolean appears_focused;
|
|
|
|
|
|
|
|
/* Calling has_shadow() here at every pre-paint is cheap
|
|
|
|
* and avoids the need to explicitly handle window type changes, which
|
|
|
|
* we would do if tried to keep track of when we might be adding or removing
|
|
|
|
* a shadow more explicitly. We only keep track of changes to the *shape* of
|
|
|
|
* the shadow with actor_x11->recompute_shadow.
|
|
|
|
*/
|
|
|
|
|
|
|
|
should_have_shadow = has_shadow (actor_x11);
|
|
|
|
appears_focused = meta_window_appears_focused (window);
|
|
|
|
|
|
|
|
if (appears_focused)
|
|
|
|
{
|
|
|
|
recompute_shadow = actor_x11->recompute_focused_shadow;
|
|
|
|
actor_x11->recompute_focused_shadow = FALSE;
|
|
|
|
shadow_location = &actor_x11->focused_shadow;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
recompute_shadow = actor_x11->recompute_unfocused_shadow;
|
|
|
|
actor_x11->recompute_unfocused_shadow = FALSE;
|
|
|
|
shadow_location = &actor_x11->unfocused_shadow;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!should_have_shadow || recompute_shadow)
|
|
|
|
{
|
|
|
|
if (*shadow_location != NULL)
|
|
|
|
{
|
|
|
|
old_shadow = *shadow_location;
|
|
|
|
*shadow_location = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!*shadow_location && should_have_shadow)
|
|
|
|
{
|
|
|
|
MetaShadowFactory *factory = actor_x11->shadow_factory;
|
|
|
|
const char *shadow_class = get_shadow_class (actor_x11);
|
|
|
|
cairo_rectangle_int_t shape_bounds;
|
|
|
|
|
|
|
|
if (!actor_x11->shadow_shape)
|
|
|
|
{
|
|
|
|
actor_x11->shadow_shape =
|
|
|
|
meta_window_shape_new (actor_x11->shape_region);
|
|
|
|
}
|
|
|
|
|
|
|
|
get_shape_bounds (actor_x11, &shape_bounds);
|
|
|
|
*shadow_location =
|
|
|
|
meta_shadow_factory_get_shadow (factory,
|
|
|
|
actor_x11->shadow_shape,
|
|
|
|
shape_bounds.width, shape_bounds.height,
|
|
|
|
shadow_class, appears_focused);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (old_shadow)
|
|
|
|
meta_shadow_unref (old_shadow);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
meta_window_actor_x11_process_damage (MetaWindowActorX11 *actor_x11,
|
|
|
|
XDamageNotifyEvent *event)
|
|
|
|
{
|
|
|
|
MetaSurfaceActor *surface;
|
|
|
|
|
|
|
|
surface = meta_window_actor_get_surface (META_WINDOW_ACTOR (actor_x11));
|
|
|
|
if (surface)
|
|
|
|
meta_surface_actor_process_damage (surface,
|
|
|
|
event->area.x,
|
|
|
|
event->area.y,
|
|
|
|
event->area.width,
|
|
|
|
event->area.height);
|
|
|
|
|
|
|
|
meta_window_actor_notify_damaged (META_WINDOW_ACTOR (actor_x11));
|
|
|
|
}
|
|
|
|
|
|
|
|
static cairo_region_t *
|
|
|
|
scan_visible_region (guchar *mask_data,
|
|
|
|
int stride,
|
|
|
|
cairo_region_t *scan_area)
|
|
|
|
{
|
|
|
|
int i, n_rects = cairo_region_num_rectangles (scan_area);
|
|
|
|
MetaRegionBuilder builder;
|
|
|
|
|
|
|
|
meta_region_builder_init (&builder);
|
|
|
|
|
|
|
|
for (i = 0; i < n_rects; i++)
|
|
|
|
{
|
|
|
|
int x, y;
|
|
|
|
cairo_rectangle_int_t rect;
|
|
|
|
|
|
|
|
cairo_region_get_rectangle (scan_area, i, &rect);
|
|
|
|
|
|
|
|
for (y = rect.y; y < (rect.y + rect.height); y++)
|
|
|
|
{
|
|
|
|
for (x = rect.x; x < (rect.x + rect.width); x++)
|
|
|
|
{
|
|
|
|
int x2 = x;
|
|
|
|
while (mask_data[y * stride + x2] == 255 && x2 < (rect.x + rect.width))
|
|
|
|
x2++;
|
|
|
|
|
|
|
|
if (x2 > x)
|
|
|
|
{
|
|
|
|
meta_region_builder_add_rectangle (&builder, x, y, x2 - x, 1);
|
|
|
|
x = x2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return meta_region_builder_finish (&builder);
|
|
|
|
}
|
|
|
|
|
2020-02-26 05:09:30 -05:00
|
|
|
static void
|
|
|
|
get_client_area_rect_from_texture (MetaWindowActorX11 *actor_x11,
|
|
|
|
MetaShapedTexture *shaped_texture,
|
|
|
|
cairo_rectangle_int_t *client_area)
|
|
|
|
{
|
|
|
|
MetaWindow *window =
|
|
|
|
meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
|
|
|
|
cairo_rectangle_int_t surface_rect = { 0 };
|
|
|
|
|
|
|
|
surface_rect.width = meta_shaped_texture_get_width (shaped_texture);
|
|
|
|
surface_rect.height = meta_shaped_texture_get_height (shaped_texture);
|
|
|
|
meta_window_x11_surface_rect_to_client_rect (window,
|
|
|
|
&surface_rect,
|
|
|
|
client_area);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
get_client_area_rect (MetaWindowActorX11 *actor_x11,
|
|
|
|
cairo_rectangle_int_t *client_area)
|
|
|
|
{
|
|
|
|
MetaSurfaceActor *surface =
|
|
|
|
meta_window_actor_get_surface (META_WINDOW_ACTOR (actor_x11));
|
|
|
|
MetaWindow *window =
|
|
|
|
meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
|
|
|
|
MetaShapedTexture *stex = meta_surface_actor_get_texture (surface);
|
|
|
|
|
|
|
|
if (!meta_window_x11_always_update_shape (window) || !stex)
|
|
|
|
{
|
|
|
|
meta_window_get_client_area_rect (window, client_area);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
get_client_area_rect_from_texture (actor_x11, stex, client_area);
|
|
|
|
}
|
|
|
|
|
2019-08-17 04:00:46 -04:00
|
|
|
static void
|
|
|
|
build_and_scan_frame_mask (MetaWindowActorX11 *actor_x11,
|
|
|
|
cairo_region_t *shape_region)
|
|
|
|
{
|
|
|
|
ClutterBackend *backend = clutter_get_default_backend ();
|
|
|
|
MetaWindow *window =
|
|
|
|
meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
|
|
|
|
CoglContext *ctx = clutter_backend_get_cogl_context (backend);
|
|
|
|
MetaSurfaceActor *surface =
|
|
|
|
meta_window_actor_get_surface (META_WINDOW_ACTOR (actor_x11));
|
|
|
|
uint8_t *mask_data;
|
|
|
|
unsigned int tex_width, tex_height;
|
|
|
|
MetaShapedTexture *stex;
|
|
|
|
CoglTexture2D *mask_texture;
|
|
|
|
int stride;
|
|
|
|
cairo_t *cr;
|
|
|
|
cairo_surface_t *image;
|
|
|
|
GError *error = NULL;
|
|
|
|
|
|
|
|
stex = meta_surface_actor_get_texture (surface);
|
|
|
|
g_return_if_fail (stex);
|
|
|
|
|
|
|
|
meta_shaped_texture_set_mask_texture (stex, NULL);
|
|
|
|
|
2020-03-04 05:08:01 -05:00
|
|
|
tex_width = meta_shaped_texture_get_width (stex);
|
|
|
|
tex_height = meta_shaped_texture_get_height (stex);
|
2019-08-17 04:00:46 -04:00
|
|
|
|
|
|
|
stride = cairo_format_stride_for_width (CAIRO_FORMAT_A8, tex_width);
|
|
|
|
|
|
|
|
/* Create data for an empty image */
|
|
|
|
mask_data = g_malloc0 (stride * tex_height);
|
|
|
|
|
|
|
|
image = cairo_image_surface_create_for_data (mask_data,
|
|
|
|
CAIRO_FORMAT_A8,
|
|
|
|
tex_width,
|
|
|
|
tex_height,
|
|
|
|
stride);
|
|
|
|
cr = cairo_create (image);
|
|
|
|
|
|
|
|
gdk_cairo_region (cr, shape_region);
|
|
|
|
cairo_fill (cr);
|
|
|
|
|
|
|
|
if (window->frame)
|
|
|
|
{
|
|
|
|
cairo_region_t *frame_paint_region, *scanned_region;
|
|
|
|
cairo_rectangle_int_t rect = { 0, 0, tex_width, tex_height };
|
2020-02-26 05:09:30 -05:00
|
|
|
cairo_rectangle_int_t client_area;
|
2020-01-16 05:42:04 -05:00
|
|
|
cairo_rectangle_int_t frame_rect;
|
|
|
|
|
2020-01-17 12:11:03 -05:00
|
|
|
/* If we update the shape regardless of the frozen state of the actor,
|
|
|
|
* as with Xwayland to avoid the black shadow effect, we ought to base
|
|
|
|
* the frame size on the buffer size rather than the reported window's
|
|
|
|
* frame size, as the buffer may not have been committed yet at this
|
|
|
|
* point.
|
|
|
|
*/
|
|
|
|
if (meta_window_x11_always_update_shape (window))
|
2020-02-26 05:09:30 -05:00
|
|
|
{
|
2020-03-10 09:44:15 -04:00
|
|
|
meta_window_x11_surface_rect_to_frame_rect (window, &rect, &frame_rect);
|
2020-02-26 05:09:30 -05:00
|
|
|
get_client_area_rect_from_texture (actor_x11, stex, &client_area);
|
|
|
|
}
|
2020-01-17 12:11:03 -05:00
|
|
|
else
|
2020-02-26 05:09:30 -05:00
|
|
|
{
|
|
|
|
meta_window_get_frame_rect (window, &frame_rect);
|
|
|
|
meta_window_get_client_area_rect (window, &client_area);
|
|
|
|
}
|
2019-08-17 04:00:46 -04:00
|
|
|
|
|
|
|
/* Make sure we don't paint the frame over the client window. */
|
|
|
|
frame_paint_region = cairo_region_create_rectangle (&rect);
|
2020-02-26 05:09:30 -05:00
|
|
|
cairo_region_subtract_rectangle (frame_paint_region, &client_area);
|
2019-08-17 04:00:46 -04:00
|
|
|
|
|
|
|
gdk_cairo_region (cr, frame_paint_region);
|
|
|
|
cairo_clip (cr);
|
|
|
|
|
2020-01-16 05:42:04 -05:00
|
|
|
meta_frame_get_mask (window->frame, &frame_rect, cr);
|
2019-08-17 04:00:46 -04:00
|
|
|
|
|
|
|
cairo_surface_flush (image);
|
|
|
|
scanned_region = scan_visible_region (mask_data, stride, frame_paint_region);
|
|
|
|
cairo_region_union (shape_region, scanned_region);
|
|
|
|
cairo_region_destroy (scanned_region);
|
|
|
|
cairo_region_destroy (frame_paint_region);
|
|
|
|
}
|
|
|
|
|
|
|
|
cairo_destroy (cr);
|
|
|
|
cairo_surface_destroy (image);
|
|
|
|
|
|
|
|
mask_texture = cogl_texture_2d_new_from_data (ctx, tex_width, tex_height,
|
|
|
|
COGL_PIXEL_FORMAT_A_8,
|
|
|
|
stride, mask_data, &error);
|
|
|
|
|
|
|
|
if (error)
|
|
|
|
{
|
|
|
|
g_warning ("Failed to allocate mask texture: %s", error->message);
|
|
|
|
g_error_free (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mask_texture)
|
|
|
|
{
|
|
|
|
meta_shaped_texture_set_mask_texture (stex, COGL_TEXTURE (mask_texture));
|
|
|
|
cogl_object_unref (mask_texture);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
meta_shaped_texture_set_mask_texture (stex, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_free (mask_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
invalidate_shadow (MetaWindowActorX11 *actor_x11)
|
|
|
|
{
|
|
|
|
actor_x11->recompute_focused_shadow = TRUE;
|
|
|
|
actor_x11->recompute_unfocused_shadow = TRUE;
|
|
|
|
|
|
|
|
if (meta_window_actor_is_frozen (META_WINDOW_ACTOR (actor_x11)))
|
|
|
|
return;
|
|
|
|
|
|
|
|
clutter_actor_queue_redraw (CLUTTER_ACTOR (actor_x11));
|
2021-04-15 09:33:56 -04:00
|
|
|
clutter_actor_invalidate_paint_volume (CLUTTER_ACTOR (actor_x11));
|
2019-08-17 04:00:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
update_shape_region (MetaWindowActorX11 *actor_x11)
|
|
|
|
{
|
|
|
|
MetaWindow *window =
|
|
|
|
meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
|
|
|
|
cairo_region_t *region = NULL;
|
2020-02-26 05:09:30 -05:00
|
|
|
cairo_rectangle_int_t client_area;
|
|
|
|
|
|
|
|
get_client_area_rect (actor_x11, &client_area);
|
2019-08-17 04:00:46 -04:00
|
|
|
|
|
|
|
if (window->frame && window->shape_region)
|
|
|
|
{
|
|
|
|
region = cairo_region_copy (window->shape_region);
|
2020-02-26 05:09:30 -05:00
|
|
|
cairo_region_translate (region, client_area.x, client_area.y);
|
2019-08-17 04:00:46 -04:00
|
|
|
}
|
|
|
|
else if (window->shape_region != NULL)
|
|
|
|
{
|
|
|
|
region = cairo_region_reference (window->shape_region);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* If we don't have a shape on the server, that means that
|
|
|
|
* we have an implicit shape of one rectangle covering the
|
|
|
|
* entire window. */
|
2020-02-26 05:09:30 -05:00
|
|
|
region = cairo_region_create_rectangle (&client_area);
|
2019-08-17 04:00:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (window->shape_region || window->frame)
|
2020-01-16 03:06:42 -05:00
|
|
|
build_and_scan_frame_mask (actor_x11, region);
|
2019-08-17 04:00:46 -04:00
|
|
|
|
|
|
|
g_clear_pointer (&actor_x11->shape_region, cairo_region_destroy);
|
|
|
|
actor_x11->shape_region = region;
|
|
|
|
|
|
|
|
g_clear_pointer (&actor_x11->shadow_shape, meta_window_shape_unref);
|
|
|
|
|
|
|
|
invalidate_shadow (actor_x11);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
update_input_region (MetaWindowActorX11 *actor_x11)
|
|
|
|
{
|
|
|
|
MetaWindow *window =
|
|
|
|
meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
|
|
|
|
MetaSurfaceActor *surface =
|
|
|
|
meta_window_actor_get_surface (META_WINDOW_ACTOR (actor_x11));
|
|
|
|
cairo_region_t *region;
|
|
|
|
|
|
|
|
if (window->shape_region && window->input_region)
|
|
|
|
{
|
|
|
|
region = cairo_region_copy (window->shape_region);
|
|
|
|
cairo_region_intersect (region, window->input_region);
|
|
|
|
}
|
|
|
|
else if (window->shape_region)
|
|
|
|
{
|
|
|
|
region = cairo_region_reference (window->shape_region);
|
|
|
|
}
|
|
|
|
else if (window->input_region)
|
|
|
|
{
|
|
|
|
region = cairo_region_reference (window->input_region);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
region = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
meta_surface_actor_set_input_region (surface, region);
|
|
|
|
cairo_region_destroy (region);
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
is_actor_maybe_transparent (MetaWindowActorX11 *actor_x11)
|
|
|
|
{
|
|
|
|
MetaSurfaceActor *surface;
|
|
|
|
MetaShapedTexture *stex;
|
|
|
|
|
|
|
|
surface = meta_window_actor_get_surface (META_WINDOW_ACTOR (actor_x11));
|
|
|
|
if (!surface)
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
if (META_IS_SURFACE_ACTOR_X11 (surface) &&
|
|
|
|
meta_surface_actor_x11_is_unredirected (META_SURFACE_ACTOR_X11 (surface)))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
stex = meta_surface_actor_get_texture (surface);
|
|
|
|
if (!meta_shaped_texture_has_alpha (stex))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
update_opaque_region (MetaWindowActorX11 *actor_x11)
|
|
|
|
{
|
|
|
|
MetaWindow *window =
|
|
|
|
meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
|
|
|
|
gboolean is_maybe_transparent;
|
|
|
|
cairo_region_t *opaque_region;
|
|
|
|
MetaSurfaceActor *surface;
|
|
|
|
|
|
|
|
is_maybe_transparent = is_actor_maybe_transparent (actor_x11);
|
|
|
|
if (is_maybe_transparent && window->opaque_region)
|
|
|
|
{
|
2020-02-26 05:09:30 -05:00
|
|
|
cairo_rectangle_int_t client_area;
|
|
|
|
|
|
|
|
get_client_area_rect (actor_x11, &client_area);
|
|
|
|
|
2019-08-17 04:00:46 -04:00
|
|
|
/* The opaque region is defined to be a part of the
|
|
|
|
* window which ARGB32 will always paint with opaque
|
|
|
|
* pixels. For these regions, we want to avoid painting
|
|
|
|
* windows and shadows beneath them.
|
|
|
|
*
|
|
|
|
* If the client gives bad coordinates where it does not
|
|
|
|
* fully paint, the behavior is defined by the specification
|
|
|
|
* to be undefined, and considered a client bug. In mutter's
|
|
|
|
* case, graphical glitches will occur.
|
|
|
|
*/
|
|
|
|
opaque_region = cairo_region_copy (window->opaque_region);
|
2020-02-26 05:09:30 -05:00
|
|
|
cairo_region_translate (opaque_region, client_area.x, client_area.y);
|
2019-08-17 04:00:46 -04:00
|
|
|
cairo_region_intersect (opaque_region, actor_x11->shape_region);
|
|
|
|
}
|
|
|
|
else if (is_maybe_transparent)
|
|
|
|
{
|
|
|
|
opaque_region = NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
opaque_region = cairo_region_reference (actor_x11->shape_region);
|
|
|
|
}
|
|
|
|
|
|
|
|
surface = meta_window_actor_get_surface (META_WINDOW_ACTOR (actor_x11));
|
|
|
|
meta_surface_actor_set_opaque_region (surface, opaque_region);
|
|
|
|
cairo_region_destroy (opaque_region);
|
|
|
|
}
|
|
|
|
|
2020-04-24 06:19:02 -04:00
|
|
|
static void
|
|
|
|
update_frame_bounds (MetaWindowActorX11 *actor_x11)
|
|
|
|
{
|
|
|
|
MetaWindow *window =
|
|
|
|
meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
|
|
|
|
|
|
|
|
g_clear_pointer (&actor_x11->frame_bounds, cairo_region_destroy);
|
|
|
|
actor_x11->frame_bounds =
|
|
|
|
cairo_region_copy (meta_window_get_frame_bounds (window));
|
|
|
|
}
|
|
|
|
|
2019-08-17 04:00:46 -04:00
|
|
|
static void
|
2020-03-03 04:26:54 -05:00
|
|
|
update_regions (MetaWindowActorX11 *actor_x11)
|
2019-08-17 04:00:46 -04:00
|
|
|
{
|
|
|
|
if (!actor_x11->needs_reshape)
|
|
|
|
return;
|
|
|
|
|
|
|
|
update_shape_region (actor_x11);
|
|
|
|
update_input_region (actor_x11);
|
|
|
|
update_opaque_region (actor_x11);
|
|
|
|
|
|
|
|
actor_x11->needs_reshape = FALSE;
|
|
|
|
}
|
|
|
|
|
2020-03-03 04:26:54 -05:00
|
|
|
static void
|
|
|
|
check_needs_reshape (MetaWindowActorX11 *actor_x11)
|
|
|
|
{
|
|
|
|
MetaWindow *window =
|
|
|
|
meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
|
|
|
|
|
|
|
|
if (meta_window_x11_always_update_shape (window))
|
|
|
|
return;
|
|
|
|
|
|
|
|
update_regions (actor_x11);
|
|
|
|
}
|
|
|
|
|
2019-08-17 04:00:46 -04:00
|
|
|
void
|
|
|
|
meta_window_actor_x11_update_shape (MetaWindowActorX11 *actor_x11)
|
|
|
|
{
|
|
|
|
MetaSurfaceActor *surface =
|
|
|
|
meta_window_actor_get_surface (META_WINDOW_ACTOR (actor_x11));
|
|
|
|
|
|
|
|
actor_x11->needs_reshape = TRUE;
|
|
|
|
|
|
|
|
if (meta_window_actor_is_frozen (META_WINDOW_ACTOR (actor_x11)))
|
|
|
|
return;
|
|
|
|
|
|
|
|
clutter_actor_queue_redraw (CLUTTER_ACTOR (surface));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
handle_updates (MetaWindowActorX11 *actor_x11)
|
|
|
|
{
|
|
|
|
MetaSurfaceActor *surface =
|
|
|
|
meta_window_actor_get_surface (META_WINDOW_ACTOR (actor_x11));
|
2020-01-16 03:06:42 -05:00
|
|
|
MetaWindow *window;
|
2019-08-17 04:00:46 -04:00
|
|
|
|
2019-12-06 09:13:03 -05:00
|
|
|
if (META_IS_SURFACE_ACTOR_X11 (surface) &&
|
|
|
|
meta_surface_actor_x11_is_unredirected (META_SURFACE_ACTOR_X11 (surface)))
|
|
|
|
return;
|
|
|
|
|
2020-01-16 03:06:42 -05:00
|
|
|
window = meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
|
2019-08-17 04:00:46 -04:00
|
|
|
if (meta_window_actor_is_frozen (META_WINDOW_ACTOR (actor_x11)))
|
|
|
|
{
|
|
|
|
/* The window is frozen due to a pending animation: we'll wait until
|
2019-12-06 09:13:03 -05:00
|
|
|
* the animation finishes to repair the window.
|
|
|
|
*
|
|
|
|
* However, with Xwayland, we still might need to update the shape
|
|
|
|
* region as the wl_buffer will be set to plain black on resize,
|
|
|
|
* which causes the shadows to look bad.
|
|
|
|
*/
|
|
|
|
if (surface && meta_window_x11_always_update_shape (window))
|
2020-02-24 18:14:40 -05:00
|
|
|
check_needs_reshape (actor_x11);
|
2019-12-06 09:13:03 -05:00
|
|
|
|
2019-08-17 04:00:46 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-05-29 13:18:47 -04:00
|
|
|
if (META_IS_SURFACE_ACTOR_X11 (surface))
|
|
|
|
{
|
|
|
|
MetaSurfaceActorX11 *surface_x11 = META_SURFACE_ACTOR_X11 (surface);
|
|
|
|
|
|
|
|
meta_surface_actor_x11_handle_updates (surface_x11);
|
|
|
|
}
|
2019-08-17 04:00:46 -04:00
|
|
|
|
2020-07-28 03:48:03 -04:00
|
|
|
if (META_IS_SURFACE_ACTOR_X11 (surface) &&
|
2020-04-24 17:21:26 -04:00
|
|
|
!meta_surface_actor_x11_is_visible (META_SURFACE_ACTOR_X11 (surface)))
|
2019-08-17 04:00:46 -04:00
|
|
|
return;
|
|
|
|
|
2020-04-24 06:19:02 -04:00
|
|
|
update_frame_bounds (actor_x11);
|
2019-08-17 04:00:46 -04:00
|
|
|
check_needs_reshape (actor_x11);
|
|
|
|
check_needs_shadow (actor_x11);
|
|
|
|
}
|
|
|
|
|
2018-12-21 15:51:02 -05:00
|
|
|
static void
|
clutter: Paint views with individual frame clocks
Replace the default master clock with multiple frame clocks, each
driving its own stage view. As each stage view represents one CRTC, this
means we draw each CRTC with its own designated frame clock,
disconnected from all the others.
For example this means we when using the native backend will never need
to wait for one monitor to vsync before painting another, so e.g. having
a 144 Hz monitor next to a 60 Hz monitor, things including both Wayland
and X11 applications and shell UI will be able to render at the
corresponding monitor refresh rate.
This also changes a warning about missed frames when sending
_NETWM_FRAME_TIMINGS messages to a debug log entry, as it's expected
that we'll start missing frames e.g. when a X11 window (via Xwayland) is
exclusively within a stage view that was not painted, while another one
was, still increasing the global frame clock.
Addititonally, this also requires the X11 window actor to schedule
timeouts for _NET_WM_FRAME_DRAWN/_NET_WM_FRAME_TIMINGS event emitting,
if the actor wasn't on any stage views, as now we'll only get the frame
callbacks on actors when they actually were painted, while in the past,
we'd invoke that vfunc when anything was painted.
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/903
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/3
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1285
2020-05-29 18:27:56 -04:00
|
|
|
handle_stage_views_changed (MetaWindowActorX11 *actor_x11)
|
|
|
|
{
|
|
|
|
ClutterActor *actor = CLUTTER_ACTOR (actor_x11);
|
|
|
|
|
window-actor/x11: Don't cache the frame-drawn frame clock
We fetch a frame clock that we schedule update on when queuing
_NET_WM_FRAME_DRAWN events. In some situations this frame clock is the
one from the stage, and if there are multiple hotplugs in a row, we
failed to update it as there were no stage views changes on the window
actor itself. As an actor updates the stage views list on layout, When a
queue_frame_drawn() call was done (typically from an X11 event) after a
second hotplug, it'd attempt to schedule an update on the frame clock
from the previous hotplug, as it didn't get notified about any
stage-views changes since for itself there was none.
Fix this by not caching the frame clock at all and just fetch it every
time.
In the majority of cases, this fetching means iterating over a very
short list (most often a single entry, rarely more), so it's very
unlikely to be of any relevance. The only situations where it might be a
heavier operation is the short time between a hotplug and a layout, as
it will attempt to traverse up to the stage to find a clock, but that's
likely only a few levels, so even that is unlikely to be an issue.
Closes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/4486
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1947>
2021-07-30 05:35:10 -04:00
|
|
|
if (actor_x11->pending_schedule_update_now)
|
clutter: Paint views with individual frame clocks
Replace the default master clock with multiple frame clocks, each
driving its own stage view. As each stage view represents one CRTC, this
means we draw each CRTC with its own designated frame clock,
disconnected from all the others.
For example this means we when using the native backend will never need
to wait for one monitor to vsync before painting another, so e.g. having
a 144 Hz monitor next to a 60 Hz monitor, things including both Wayland
and X11 applications and shell UI will be able to render at the
corresponding monitor refresh rate.
This also changes a warning about missed frames when sending
_NETWM_FRAME_TIMINGS messages to a debug log entry, as it's expected
that we'll start missing frames e.g. when a X11 window (via Xwayland) is
exclusively within a stage view that was not painted, while another one
was, still increasing the global frame clock.
Addititonally, this also requires the X11 window actor to schedule
timeouts for _NET_WM_FRAME_DRAWN/_NET_WM_FRAME_TIMINGS event emitting,
if the actor wasn't on any stage views, as now we'll only get the frame
callbacks on actors when they actually were painted, while in the past,
we'd invoke that vfunc when anything was painted.
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/903
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/3
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1285
2020-05-29 18:27:56 -04:00
|
|
|
{
|
window-actor/x11: Don't cache the frame-drawn frame clock
We fetch a frame clock that we schedule update on when queuing
_NET_WM_FRAME_DRAWN events. In some situations this frame clock is the
one from the stage, and if there are multiple hotplugs in a row, we
failed to update it as there were no stage views changes on the window
actor itself. As an actor updates the stage views list on layout, When a
queue_frame_drawn() call was done (typically from an X11 event) after a
second hotplug, it'd attempt to schedule an update on the frame clock
from the previous hotplug, as it didn't get notified about any
stage-views changes since for itself there was none.
Fix this by not caching the frame clock at all and just fetch it every
time.
In the majority of cases, this fetching means iterating over a very
short list (most often a single entry, rarely more), so it's very
unlikely to be of any relevance. The only situations where it might be a
heavier operation is the short time between a hotplug and a layout, as
it will attempt to traverse up to the stage to find a clock, but that's
likely only a few levels, so even that is unlikely to be an issue.
Closes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/4486
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1947>
2021-07-30 05:35:10 -04:00
|
|
|
ClutterFrameClock *frame_clock;
|
|
|
|
|
|
|
|
frame_clock = clutter_actor_pick_frame_clock (actor, NULL);
|
|
|
|
if (frame_clock)
|
|
|
|
{
|
|
|
|
clutter_frame_clock_schedule_update_now (frame_clock);
|
|
|
|
actor_x11->pending_schedule_update_now = FALSE;
|
|
|
|
}
|
clutter: Paint views with individual frame clocks
Replace the default master clock with multiple frame clocks, each
driving its own stage view. As each stage view represents one CRTC, this
means we draw each CRTC with its own designated frame clock,
disconnected from all the others.
For example this means we when using the native backend will never need
to wait for one monitor to vsync before painting another, so e.g. having
a 144 Hz monitor next to a 60 Hz monitor, things including both Wayland
and X11 applications and shell UI will be able to render at the
corresponding monitor refresh rate.
This also changes a warning about missed frames when sending
_NETWM_FRAME_TIMINGS messages to a debug log entry, as it's expected
that we'll start missing frames e.g. when a X11 window (via Xwayland) is
exclusively within a stage view that was not painted, while another one
was, still increasing the global frame clock.
Addititonally, this also requires the X11 window actor to schedule
timeouts for _NET_WM_FRAME_DRAWN/_NET_WM_FRAME_TIMINGS event emitting,
if the actor wasn't on any stage views, as now we'll only get the frame
callbacks on actors when they actually were painted, while in the past,
we'd invoke that vfunc when anything was painted.
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/903
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/3
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1285
2020-05-29 18:27:56 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
meta_window_actor_x11_before_paint (MetaWindowActor *actor,
|
|
|
|
ClutterStageView *stage_view)
|
2018-12-21 15:51:02 -05:00
|
|
|
{
|
|
|
|
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor);
|
|
|
|
|
2019-08-17 04:00:46 -04:00
|
|
|
handle_updates (actor_x11);
|
|
|
|
|
2018-12-21 15:51:02 -05:00
|
|
|
assign_frame_counter_to_frames (actor_x11);
|
|
|
|
}
|
|
|
|
|
2019-08-17 04:00:46 -04:00
|
|
|
static void
|
2019-11-13 16:21:58 -05:00
|
|
|
meta_window_actor_x11_paint (ClutterActor *actor,
|
|
|
|
ClutterPaintContext *paint_context)
|
2019-08-17 04:00:46 -04:00
|
|
|
{
|
|
|
|
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor);
|
|
|
|
MetaWindow *window;
|
|
|
|
gboolean appears_focused;
|
|
|
|
MetaShadow *shadow;
|
|
|
|
|
|
|
|
/* This window got damage when obscured; we set up a timer
|
|
|
|
* to send frame completion events, but since we're drawing
|
|
|
|
* the window now (for some other reason) cancel the timer
|
|
|
|
* and send the completion events normally */
|
|
|
|
if (actor_x11->send_frame_messages_timer != 0)
|
|
|
|
{
|
|
|
|
remove_frame_messages_timer (actor_x11);
|
|
|
|
assign_frame_counter_to_frames (actor_x11);
|
|
|
|
}
|
|
|
|
|
|
|
|
window = meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
|
|
|
|
appears_focused = meta_window_appears_focused (window);
|
|
|
|
shadow = appears_focused ? actor_x11->focused_shadow
|
|
|
|
: actor_x11->unfocused_shadow;
|
|
|
|
|
|
|
|
if (shadow)
|
|
|
|
{
|
|
|
|
MetaShadowParams params;
|
|
|
|
cairo_rectangle_int_t shape_bounds;
|
|
|
|
cairo_region_t *clip = actor_x11->shadow_clip;
|
2019-11-22 05:41:04 -05:00
|
|
|
CoglFramebuffer *framebuffer;
|
2019-08-17 04:00:46 -04:00
|
|
|
|
|
|
|
get_shape_bounds (actor_x11, &shape_bounds);
|
|
|
|
get_shadow_params (actor_x11, appears_focused, ¶ms);
|
|
|
|
|
|
|
|
/* The frame bounds are already subtracted from actor_x11->shadow_clip
|
|
|
|
* if that exists.
|
|
|
|
*/
|
|
|
|
if (!clip && clip_shadow_under_window (actor_x11))
|
|
|
|
{
|
|
|
|
cairo_rectangle_int_t bounds;
|
|
|
|
|
|
|
|
get_shadow_bounds (actor_x11, appears_focused, &bounds);
|
|
|
|
clip = cairo_region_create_rectangle (&bounds);
|
|
|
|
|
2020-04-24 06:19:02 -04:00
|
|
|
if (actor_x11->frame_bounds)
|
|
|
|
cairo_region_subtract (clip, actor_x11->frame_bounds);
|
2019-08-17 04:00:46 -04:00
|
|
|
}
|
|
|
|
|
2019-11-22 05:41:04 -05:00
|
|
|
framebuffer = clutter_paint_context_get_framebuffer (paint_context);
|
2019-08-17 04:00:46 -04:00
|
|
|
meta_shadow_paint (shadow,
|
2019-11-22 05:41:04 -05:00
|
|
|
framebuffer,
|
2019-08-17 04:00:46 -04:00
|
|
|
params.x_offset + shape_bounds.x,
|
|
|
|
params.y_offset + shape_bounds.y,
|
|
|
|
shape_bounds.width,
|
|
|
|
shape_bounds.height,
|
|
|
|
(clutter_actor_get_paint_opacity (actor) *
|
|
|
|
params.opacity * window->opacity) / (255 * 255),
|
|
|
|
clip,
|
|
|
|
clip_shadow_under_window (actor_x11));
|
|
|
|
|
|
|
|
if (clip && clip != actor_x11->shadow_clip)
|
|
|
|
cairo_region_destroy (clip);
|
|
|
|
}
|
|
|
|
|
2019-11-13 16:21:58 -05:00
|
|
|
CLUTTER_ACTOR_CLASS (meta_window_actor_x11_parent_class)->paint (actor,
|
|
|
|
paint_context);
|
2019-08-17 04:00:46 -04:00
|
|
|
}
|
|
|
|
|
2018-12-21 15:51:02 -05:00
|
|
|
static void
|
clutter: Paint views with individual frame clocks
Replace the default master clock with multiple frame clocks, each
driving its own stage view. As each stage view represents one CRTC, this
means we draw each CRTC with its own designated frame clock,
disconnected from all the others.
For example this means we when using the native backend will never need
to wait for one monitor to vsync before painting another, so e.g. having
a 144 Hz monitor next to a 60 Hz monitor, things including both Wayland
and X11 applications and shell UI will be able to render at the
corresponding monitor refresh rate.
This also changes a warning about missed frames when sending
_NETWM_FRAME_TIMINGS messages to a debug log entry, as it's expected
that we'll start missing frames e.g. when a X11 window (via Xwayland) is
exclusively within a stage view that was not painted, while another one
was, still increasing the global frame clock.
Addititonally, this also requires the X11 window actor to schedule
timeouts for _NET_WM_FRAME_DRAWN/_NET_WM_FRAME_TIMINGS event emitting,
if the actor wasn't on any stage views, as now we'll only get the frame
callbacks on actors when they actually were painted, while in the past,
we'd invoke that vfunc when anything was painted.
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/903
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/3
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1285
2020-05-29 18:27:56 -04:00
|
|
|
meta_window_actor_x11_after_paint (MetaWindowActor *actor,
|
|
|
|
ClutterStageView *stage_view)
|
2018-12-21 15:51:02 -05:00
|
|
|
{
|
|
|
|
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor);
|
2022-09-14 18:30:23 -04:00
|
|
|
MetaSyncCounter *sync_counter;
|
2019-12-02 08:01:30 -05:00
|
|
|
MetaWindow *window;
|
2018-12-21 15:51:02 -05:00
|
|
|
|
|
|
|
actor_x11->repaint_scheduled = FALSE;
|
|
|
|
|
|
|
|
if (meta_window_actor_is_destroyed (actor))
|
|
|
|
return;
|
|
|
|
|
2022-09-14 18:30:23 -04:00
|
|
|
window = meta_window_actor_get_meta_window (actor);
|
|
|
|
|
2018-12-21 15:51:02 -05:00
|
|
|
/* If the window had damage, but wasn't actually redrawn because
|
|
|
|
* it is obscured, we should wait until timer expiration before
|
|
|
|
* sending _NET_WM_FRAME_* messages.
|
|
|
|
*/
|
2022-09-14 18:30:23 -04:00
|
|
|
if (actor_x11->send_frame_messages_timer == 0)
|
2018-12-21 15:51:02 -05:00
|
|
|
{
|
2022-09-14 18:30:23 -04:00
|
|
|
sync_counter = meta_window_x11_get_sync_counter (window);
|
|
|
|
meta_sync_counter_send_frame_drawn (sync_counter);
|
2022-09-09 09:54:28 -04:00
|
|
|
|
|
|
|
if (window->frame)
|
|
|
|
{
|
|
|
|
sync_counter = meta_frame_get_sync_counter (window->frame);
|
|
|
|
meta_sync_counter_send_frame_drawn (sync_counter);
|
|
|
|
}
|
2018-12-21 15:51:02 -05:00
|
|
|
}
|
2019-12-02 08:01:30 -05:00
|
|
|
|
|
|
|
/* This is for Xwayland, and a no-op on plain Xorg */
|
|
|
|
if (meta_window_x11_should_thaw_after_paint (window))
|
|
|
|
{
|
|
|
|
meta_window_x11_thaw_commits (window);
|
|
|
|
meta_window_x11_set_thaw_after_paint (window, FALSE);
|
|
|
|
}
|
2022-09-16 07:06:23 -04:00
|
|
|
|
|
|
|
if (window == window->display->grab_window &&
|
|
|
|
meta_grab_op_is_resizing (window->display->grab_op))
|
|
|
|
{
|
|
|
|
/* This means we are ready for another configure;
|
|
|
|
* no pointer round trip here, to keep in sync */
|
|
|
|
meta_window_x11_check_update_resize (window);
|
|
|
|
}
|
2018-12-21 15:51:02 -05:00
|
|
|
}
|
|
|
|
|
2019-08-17 04:00:46 -04:00
|
|
|
static gboolean
|
|
|
|
meta_window_actor_x11_get_paint_volume (ClutterActor *actor,
|
|
|
|
ClutterPaintVolume *volume)
|
|
|
|
{
|
|
|
|
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor);
|
|
|
|
MetaWindow *window;
|
|
|
|
gboolean appears_focused;
|
|
|
|
MetaSurfaceActor *surface;
|
|
|
|
|
|
|
|
/* The paint volume is computed before paint functions are called
|
|
|
|
* so our bounds might not be updated yet. Force an update. */
|
|
|
|
handle_updates (actor_x11);
|
|
|
|
|
|
|
|
window = meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
|
|
|
|
appears_focused = meta_window_appears_focused (window);
|
|
|
|
if (appears_focused ? actor_x11->focused_shadow : actor_x11->unfocused_shadow)
|
|
|
|
{
|
|
|
|
cairo_rectangle_int_t shadow_bounds;
|
|
|
|
ClutterActorBox shadow_box;
|
|
|
|
|
|
|
|
/* We could compute an full clip region as we do for the window
|
|
|
|
* texture, but the shadow is relatively cheap to draw, and
|
|
|
|
* a little more complex to clip, so we just catch the case where
|
|
|
|
* the shadow is completely obscured and doesn't need to be drawn
|
|
|
|
* at all.
|
|
|
|
*/
|
|
|
|
|
|
|
|
get_shadow_bounds (actor_x11, appears_focused, &shadow_bounds);
|
|
|
|
shadow_box.x1 = shadow_bounds.x;
|
|
|
|
shadow_box.x2 = shadow_bounds.x + shadow_bounds.width;
|
|
|
|
shadow_box.y1 = shadow_bounds.y;
|
|
|
|
shadow_box.y2 = shadow_bounds.y + shadow_bounds.height;
|
|
|
|
|
|
|
|
clutter_paint_volume_union_box (volume, &shadow_box);
|
|
|
|
}
|
|
|
|
|
|
|
|
surface = meta_window_actor_get_surface (META_WINDOW_ACTOR (actor_x11));
|
|
|
|
if (surface)
|
|
|
|
{
|
|
|
|
ClutterActor *surface_actor = CLUTTER_ACTOR (surface);
|
|
|
|
const ClutterPaintVolume *child_volume;
|
|
|
|
|
|
|
|
child_volume = clutter_actor_get_transformed_paint_volume (surface_actor,
|
|
|
|
actor);
|
|
|
|
if (!child_volume)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
clutter_paint_volume_union (volume, child_volume);
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2018-12-21 15:51:02 -05:00
|
|
|
static void
|
|
|
|
meta_window_actor_x11_queue_destroy (MetaWindowActor *actor)
|
|
|
|
{
|
|
|
|
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor);
|
|
|
|
|
|
|
|
if (actor_x11->send_frame_messages_timer != 0)
|
|
|
|
remove_frame_messages_timer (actor_x11);
|
|
|
|
}
|
|
|
|
|
2019-12-09 09:06:58 -05:00
|
|
|
static void
|
|
|
|
meta_window_actor_x11_set_frozen (MetaWindowActor *actor,
|
|
|
|
gboolean frozen)
|
|
|
|
{
|
2019-11-19 08:30:44 -05:00
|
|
|
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor);
|
|
|
|
MetaWindow *window = meta_window_actor_get_meta_window (actor);
|
|
|
|
|
|
|
|
if (actor_x11->is_frozen == frozen)
|
|
|
|
return;
|
|
|
|
|
|
|
|
actor_x11->is_frozen = frozen;
|
2022-05-03 12:47:57 -04:00
|
|
|
meta_surface_actor_set_frozen (meta_window_actor_get_surface (actor), frozen);
|
2019-11-19 08:30:44 -05:00
|
|
|
|
|
|
|
if (frozen)
|
|
|
|
meta_window_x11_freeze_commits (window);
|
|
|
|
else
|
|
|
|
meta_window_x11_thaw_commits (window);
|
2019-12-09 09:06:58 -05:00
|
|
|
}
|
|
|
|
|
2020-03-03 04:26:54 -05:00
|
|
|
static void
|
|
|
|
meta_window_actor_x11_update_regions (MetaWindowActor *actor)
|
|
|
|
{
|
|
|
|
update_regions (META_WINDOW_ACTOR_X11 (actor));
|
|
|
|
}
|
|
|
|
|
2021-01-22 10:26:17 -05:00
|
|
|
static gboolean
|
|
|
|
meta_window_actor_x11_can_freeze_commits (MetaWindowActor *actor)
|
|
|
|
{
|
|
|
|
ClutterActor *clutter_actor = CLUTTER_ACTOR (actor);
|
|
|
|
|
|
|
|
return clutter_actor_is_mapped (clutter_actor);
|
|
|
|
}
|
|
|
|
|
2022-03-15 07:21:17 -04:00
|
|
|
static gboolean
|
|
|
|
meta_window_actor_x11_is_single_surface_actor (MetaWindowActor *actor)
|
|
|
|
{
|
|
|
|
return clutter_actor_get_n_children (CLUTTER_ACTOR (actor)) == 1;
|
|
|
|
}
|
|
|
|
|
2022-05-03 12:47:57 -04:00
|
|
|
static void
|
|
|
|
meta_window_actor_x11_sync_geometry (MetaWindowActor *actor,
|
|
|
|
const MetaRectangle *actor_rect)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2018-12-21 15:51:02 -05:00
|
|
|
static void
|
2019-08-17 04:00:46 -04:00
|
|
|
meta_window_actor_x11_set_property (GObject *object,
|
|
|
|
guint prop_id,
|
|
|
|
const GValue *value,
|
|
|
|
GParamSpec *pspec)
|
2018-12-21 15:51:02 -05:00
|
|
|
{
|
2019-08-17 04:00:46 -04:00
|
|
|
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (object);
|
2018-12-21 15:51:02 -05:00
|
|
|
|
2019-08-17 04:00:46 -04:00
|
|
|
switch (prop_id)
|
2018-12-21 15:51:02 -05:00
|
|
|
{
|
2019-08-17 04:00:46 -04:00
|
|
|
case PROP_SHADOW_MODE:
|
|
|
|
{
|
|
|
|
MetaShadowMode newv = g_value_get_enum (value);
|
|
|
|
|
|
|
|
if (newv == actor_x11->shadow_mode)
|
|
|
|
return;
|
|
|
|
|
|
|
|
actor_x11->shadow_mode = newv;
|
|
|
|
|
|
|
|
invalidate_shadow (actor_x11);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case PROP_SHADOW_CLASS:
|
|
|
|
{
|
|
|
|
const char *newv = g_value_get_string (value);
|
|
|
|
|
|
|
|
if (g_strcmp0 (newv, actor_x11->shadow_class) == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
g_free (actor_x11->shadow_class);
|
|
|
|
actor_x11->shadow_class = g_strdup (newv);
|
|
|
|
|
|
|
|
invalidate_shadow (actor_x11);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
|
|
break;
|
2018-12-21 15:51:02 -05:00
|
|
|
}
|
2019-08-17 04:00:46 -04:00
|
|
|
}
|
2018-12-21 15:51:02 -05:00
|
|
|
|
2019-08-17 04:00:46 -04:00
|
|
|
static void
|
|
|
|
meta_window_actor_x11_get_property (GObject *object,
|
|
|
|
guint prop_id,
|
|
|
|
GValue *value,
|
|
|
|
GParamSpec *pspec)
|
|
|
|
{
|
|
|
|
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (object);
|
|
|
|
|
|
|
|
switch (prop_id)
|
|
|
|
{
|
|
|
|
case PROP_SHADOW_MODE:
|
|
|
|
g_value_set_enum (value, actor_x11->shadow_mode);
|
|
|
|
break;
|
|
|
|
case PROP_SHADOW_CLASS:
|
|
|
|
g_value_set_string (value, actor_x11->shadow_class);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
|
|
break;
|
|
|
|
}
|
2018-12-21 15:51:02 -05:00
|
|
|
}
|
|
|
|
|
2019-01-03 13:05:48 -05:00
|
|
|
static void
|
|
|
|
meta_window_actor_x11_constructed (GObject *object)
|
|
|
|
{
|
2019-08-17 04:00:46 -04:00
|
|
|
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (object);
|
|
|
|
MetaWindowActor *actor = META_WINDOW_ACTOR (actor_x11);
|
|
|
|
MetaWindow *window = meta_window_actor_get_meta_window (actor);
|
2022-09-07 08:26:54 -04:00
|
|
|
MetaSyncCounter *sync_counter = meta_window_x11_get_sync_counter (window);
|
2019-08-17 04:00:46 -04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Start off with an empty shape region to maintain the invariant that it's
|
|
|
|
* always set.
|
|
|
|
*/
|
|
|
|
actor_x11->shape_region = cairo_region_create ();
|
2019-01-03 13:05:48 -05:00
|
|
|
|
|
|
|
G_OBJECT_CLASS (meta_window_actor_x11_parent_class)->constructed (object);
|
|
|
|
|
|
|
|
/* If a window doesn't start off with updates frozen, we should
|
|
|
|
* we should send a _NET_WM_FRAME_DRAWN immediately after the first drawn.
|
|
|
|
*/
|
2022-09-07 08:26:54 -04:00
|
|
|
if (sync_counter->extended_sync_request_counter &&
|
2019-01-03 13:05:48 -05:00
|
|
|
!meta_window_updates_are_frozen (window))
|
2022-09-14 18:30:23 -04:00
|
|
|
{
|
|
|
|
meta_sync_counter_queue_frame_drawn (sync_counter);
|
|
|
|
meta_window_actor_queue_frame_drawn (actor, FALSE);
|
|
|
|
}
|
2019-08-17 04:00:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
meta_window_actor_x11_cull_out (MetaCullable *cullable,
|
|
|
|
cairo_region_t *unobscured_region,
|
|
|
|
cairo_region_t *clip_region)
|
|
|
|
{
|
2020-02-16 13:40:48 -05:00
|
|
|
MetaWindowActorX11 *self = META_WINDOW_ACTOR_X11 (cullable);
|
|
|
|
|
2022-03-15 06:34:26 -04:00
|
|
|
meta_cullable_cull_out_children (cullable,
|
|
|
|
unobscured_region,
|
|
|
|
clip_region);
|
2019-08-17 04:00:46 -04:00
|
|
|
|
2020-02-16 13:40:48 -05:00
|
|
|
set_clip_region_beneath (self, clip_region);
|
2019-08-17 04:00:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
meta_window_actor_x11_reset_culling (MetaCullable *cullable)
|
|
|
|
{
|
|
|
|
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (cullable);
|
|
|
|
|
|
|
|
g_clear_pointer (&actor_x11->shadow_clip, cairo_region_destroy);
|
|
|
|
|
2022-03-15 06:34:26 -04:00
|
|
|
meta_cullable_reset_culling_children (cullable);
|
2019-08-17 04:00:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
cullable_iface_init (MetaCullableInterface *iface)
|
|
|
|
{
|
|
|
|
iface->cull_out = meta_window_actor_x11_cull_out;
|
|
|
|
iface->reset_culling = meta_window_actor_x11_reset_culling;
|
2019-01-03 13:05:48 -05:00
|
|
|
}
|
|
|
|
|
2018-12-21 15:51:02 -05:00
|
|
|
static void
|
|
|
|
meta_window_actor_x11_dispose (GObject *object)
|
|
|
|
{
|
|
|
|
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (object);
|
2019-06-27 13:33:24 -04:00
|
|
|
MetaSurfaceActor *surface_actor;
|
2018-12-21 15:51:02 -05:00
|
|
|
|
2019-08-17 04:00:46 -04:00
|
|
|
g_clear_signal_handler (&actor_x11->shadow_factory_changed_handler_id,
|
|
|
|
actor_x11->shadow_factory);
|
|
|
|
|
2018-12-21 15:51:02 -05:00
|
|
|
if (actor_x11->send_frame_messages_timer != 0)
|
|
|
|
remove_frame_messages_timer (actor_x11);
|
|
|
|
|
2019-06-27 13:33:24 -04:00
|
|
|
surface_actor = meta_window_actor_get_surface (META_WINDOW_ACTOR (actor_x11));
|
|
|
|
if (surface_actor)
|
2019-08-17 04:00:46 -04:00
|
|
|
{
|
|
|
|
g_clear_signal_handler (&actor_x11->repaint_scheduled_id, surface_actor);
|
|
|
|
g_clear_signal_handler (&actor_x11->size_changed_id, surface_actor);
|
2022-05-03 12:47:57 -04:00
|
|
|
|
|
|
|
clutter_actor_remove_child (CLUTTER_ACTOR (object),
|
|
|
|
CLUTTER_ACTOR (surface_actor));
|
2019-08-17 04:00:46 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
g_clear_pointer (&actor_x11->shape_region, cairo_region_destroy);
|
|
|
|
g_clear_pointer (&actor_x11->shadow_clip, cairo_region_destroy);
|
2020-04-24 06:19:02 -04:00
|
|
|
g_clear_pointer (&actor_x11->frame_bounds, cairo_region_destroy);
|
2019-08-17 04:00:46 -04:00
|
|
|
|
|
|
|
g_clear_pointer (&actor_x11->shadow_class, g_free);
|
|
|
|
g_clear_pointer (&actor_x11->focused_shadow, meta_shadow_unref);
|
|
|
|
g_clear_pointer (&actor_x11->unfocused_shadow, meta_shadow_unref);
|
|
|
|
g_clear_pointer (&actor_x11->shadow_shape, meta_window_shape_unref);
|
2019-06-27 13:33:24 -04:00
|
|
|
|
2018-12-21 15:51:02 -05:00
|
|
|
G_OBJECT_CLASS (meta_window_actor_x11_parent_class)->dispose (object);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
meta_window_actor_x11_finalize (GObject *object)
|
|
|
|
{
|
|
|
|
G_OBJECT_CLASS (meta_window_actor_x11_parent_class)->finalize (object);
|
|
|
|
}
|
|
|
|
|
2018-12-21 15:35:18 -05:00
|
|
|
static void
|
|
|
|
meta_window_actor_x11_class_init (MetaWindowActorX11Class *klass)
|
|
|
|
{
|
2018-12-21 15:51:02 -05:00
|
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
|
|
|
MetaWindowActorClass *window_actor_class = META_WINDOW_ACTOR_CLASS (klass);
|
2019-08-17 04:00:46 -04:00
|
|
|
GParamSpec *pspec;
|
2018-12-21 15:51:02 -05:00
|
|
|
|
|
|
|
window_actor_class->frame_complete = meta_window_actor_x11_frame_complete;
|
2022-05-11 16:32:33 -04:00
|
|
|
window_actor_class->get_scanout_candidate = meta_window_actor_x11_get_scanout_candidate;
|
2019-06-27 13:33:24 -04:00
|
|
|
window_actor_class->assign_surface_actor = meta_window_actor_x11_assign_surface_actor;
|
2018-12-21 15:51:02 -05:00
|
|
|
window_actor_class->queue_frame_drawn = meta_window_actor_x11_queue_frame_drawn;
|
2020-05-29 18:02:42 -04:00
|
|
|
window_actor_class->before_paint = meta_window_actor_x11_before_paint;
|
|
|
|
window_actor_class->after_paint = meta_window_actor_x11_after_paint;
|
2018-12-21 15:51:02 -05:00
|
|
|
window_actor_class->queue_destroy = meta_window_actor_x11_queue_destroy;
|
2019-12-09 09:06:58 -05:00
|
|
|
window_actor_class->set_frozen = meta_window_actor_x11_set_frozen;
|
2020-03-03 04:26:54 -05:00
|
|
|
window_actor_class->update_regions = meta_window_actor_x11_update_regions;
|
2021-01-22 10:26:17 -05:00
|
|
|
window_actor_class->can_freeze_commits = meta_window_actor_x11_can_freeze_commits;
|
2022-05-03 12:47:57 -04:00
|
|
|
window_actor_class->sync_geometry = meta_window_actor_x11_sync_geometry;
|
2022-03-15 07:21:17 -04:00
|
|
|
window_actor_class->is_single_surface_actor = meta_window_actor_x11_is_single_surface_actor;
|
2018-12-21 15:51:02 -05:00
|
|
|
|
|
|
|
actor_class->paint = meta_window_actor_x11_paint;
|
2019-08-17 04:00:46 -04:00
|
|
|
actor_class->get_paint_volume = meta_window_actor_x11_get_paint_volume;
|
2018-12-21 15:51:02 -05:00
|
|
|
|
2019-01-03 13:05:48 -05:00
|
|
|
object_class->constructed = meta_window_actor_x11_constructed;
|
2019-08-17 04:00:46 -04:00
|
|
|
object_class->set_property = meta_window_actor_x11_set_property;
|
|
|
|
object_class->get_property = meta_window_actor_x11_get_property;
|
2018-12-21 15:51:02 -05:00
|
|
|
object_class->dispose = meta_window_actor_x11_dispose;
|
|
|
|
object_class->finalize = meta_window_actor_x11_finalize;
|
2019-08-17 04:00:46 -04:00
|
|
|
|
|
|
|
pspec = g_param_spec_enum ("shadow-mode",
|
|
|
|
"Shadow mode",
|
|
|
|
"Decides when to paint shadows",
|
|
|
|
META_TYPE_SHADOW_MODE,
|
|
|
|
META_SHADOW_MODE_AUTO,
|
|
|
|
G_PARAM_READWRITE);
|
|
|
|
|
|
|
|
g_object_class_install_property (object_class,
|
|
|
|
PROP_SHADOW_MODE,
|
|
|
|
pspec);
|
|
|
|
|
|
|
|
pspec = g_param_spec_string ("shadow-class",
|
|
|
|
"Name of the shadow class for this window.",
|
|
|
|
"NULL means to use the default shadow class for this window type",
|
|
|
|
NULL,
|
|
|
|
G_PARAM_READWRITE);
|
|
|
|
|
|
|
|
g_object_class_install_property (object_class,
|
|
|
|
PROP_SHADOW_CLASS,
|
|
|
|
pspec);
|
2018-12-21 15:35:18 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
meta_window_actor_x11_init (MetaWindowActorX11 *self)
|
|
|
|
{
|
2019-08-17 04:00:46 -04:00
|
|
|
/* We do this now since we might be going right back into the frozen state. */
|
|
|
|
g_signal_connect (self, "thawed", G_CALLBACK (handle_updates), NULL);
|
|
|
|
|
clutter: Paint views with individual frame clocks
Replace the default master clock with multiple frame clocks, each
driving its own stage view. As each stage view represents one CRTC, this
means we draw each CRTC with its own designated frame clock,
disconnected from all the others.
For example this means we when using the native backend will never need
to wait for one monitor to vsync before painting another, so e.g. having
a 144 Hz monitor next to a 60 Hz monitor, things including both Wayland
and X11 applications and shell UI will be able to render at the
corresponding monitor refresh rate.
This also changes a warning about missed frames when sending
_NETWM_FRAME_TIMINGS messages to a debug log entry, as it's expected
that we'll start missing frames e.g. when a X11 window (via Xwayland) is
exclusively within a stage view that was not painted, while another one
was, still increasing the global frame clock.
Addititonally, this also requires the X11 window actor to schedule
timeouts for _NET_WM_FRAME_DRAWN/_NET_WM_FRAME_TIMINGS event emitting,
if the actor wasn't on any stage views, as now we'll only get the frame
callbacks on actors when they actually were painted, while in the past,
we'd invoke that vfunc when anything was painted.
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/903
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/3
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1285
2020-05-29 18:27:56 -04:00
|
|
|
g_signal_connect (self, "stage-views-changed",
|
|
|
|
G_CALLBACK (handle_stage_views_changed), NULL);
|
|
|
|
|
2019-08-17 04:00:46 -04:00
|
|
|
self->shadow_factory = meta_shadow_factory_get_default ();
|
|
|
|
self->shadow_factory_changed_handler_id =
|
2020-06-11 18:19:41 -04:00
|
|
|
g_signal_connect_swapped (self->shadow_factory,
|
|
|
|
"changed",
|
|
|
|
G_CALLBACK (invalidate_shadow),
|
|
|
|
self);
|
2018-12-21 15:35:18 -05:00
|
|
|
}
|