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"
|
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
|
|
|
|
|
|
|
/* List of FrameData for recent frames */
|
|
|
|
GList *frames;
|
|
|
|
|
|
|
|
guint send_frame_messages_timer;
|
|
|
|
int64_t frame_drawn_time;
|
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;
|
|
|
|
ClutterFrameClock *frame_clock;
|
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
|
|
|
|
|
|
|
/* If set, the client needs to be sent a _NET_WM_FRAME_DRAWN
|
|
|
|
* client message for one or more messages in ->frames */
|
|
|
|
gboolean needs_frame_drawn;
|
|
|
|
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 MetaCullableInterface *cullable_parent_iface;
|
|
|
|
|
|
|
|
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
|
|
|
/* Each time the application updates the sync request counter to a new even value
|
|
|
|
* value, we queue a frame into the windows list of frames. Once we're painting
|
|
|
|
* an update "in response" to the window, we fill in frame_counter with the
|
|
|
|
* Cogl counter for that frame, and send _NET_WM_FRAME_DRAWN at the end of the
|
|
|
|
* frame. _NET_WM_FRAME_TIMINGS is sent when we get a frame_complete callback.
|
|
|
|
*
|
|
|
|
* As an exception, if a window is completely obscured, we try to throttle drawning
|
|
|
|
* to a slower frame rate. In this case, frame_counter stays -1 until
|
|
|
|
* send_frame_message_timeout() runs, at which point we send both the
|
|
|
|
* _NET_WM_FRAME_DRAWN and _NET_WM_FRAME_TIMINGS messages.
|
|
|
|
*/
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
uint64_t sync_request_serial;
|
|
|
|
int64_t frame_counter;
|
|
|
|
int64_t frame_drawn_time;
|
|
|
|
} FrameData;
|
|
|
|
|
|
|
|
static void
|
|
|
|
frame_data_free (FrameData *frame)
|
|
|
|
{
|
|
|
|
g_slice_free (FrameData, frame);
|
|
|
|
}
|
|
|
|
|
|
|
|
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 void
|
|
|
|
do_send_frame_drawn (MetaWindowActorX11 *actor_x11,
|
|
|
|
FrameData *frame)
|
|
|
|
{
|
|
|
|
MetaWindow *window =
|
|
|
|
meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
|
|
|
|
MetaDisplay *display = meta_window_get_display (window);
|
|
|
|
Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display);
|
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
|
|
|
|
|
|
|
XClientMessageEvent ev = { 0, };
|
|
|
|
|
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
|
|
|
frame->frame_drawn_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
|
|
|
actor_x11->frame_drawn_time = frame->frame_drawn_time;
|
|
|
|
|
|
|
|
ev.type = ClientMessage;
|
|
|
|
ev.window = meta_window_get_xwindow (window);
|
|
|
|
ev.message_type = display->x11_display->atom__NET_WM_FRAME_DRAWN;
|
|
|
|
ev.format = 32;
|
|
|
|
ev.data.l[0] = frame->sync_request_serial & G_GUINT64_CONSTANT (0xffffffff);
|
|
|
|
ev.data.l[1] = frame->sync_request_serial >> 32;
|
|
|
|
ev.data.l[2] = frame->frame_drawn_time & G_GUINT64_CONSTANT (0xffffffff);
|
|
|
|
ev.data.l[3] = frame->frame_drawn_time >> 32;
|
|
|
|
|
|
|
|
meta_x11_error_trap_push (display->x11_display);
|
|
|
|
XSendEvent (xdisplay, ev.window, False, 0, (XEvent *) &ev);
|
|
|
|
XFlush (xdisplay);
|
|
|
|
meta_x11_error_trap_pop (display->x11_display);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
do_send_frame_timings (MetaWindowActorX11 *actor_x11,
|
|
|
|
FrameData *frame,
|
|
|
|
int refresh_interval,
|
|
|
|
int64_t presentation_time)
|
|
|
|
{
|
|
|
|
MetaWindow *window =
|
|
|
|
meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
|
|
|
|
MetaDisplay *display = meta_window_get_display (window);
|
|
|
|
Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display);
|
|
|
|
|
|
|
|
XClientMessageEvent ev = { 0, };
|
|
|
|
|
|
|
|
ev.type = ClientMessage;
|
|
|
|
ev.window = meta_window_get_xwindow (window);
|
|
|
|
ev.message_type = display->x11_display->atom__NET_WM_FRAME_TIMINGS;
|
|
|
|
ev.format = 32;
|
|
|
|
ev.data.l[0] = frame->sync_request_serial & G_GUINT64_CONSTANT (0xffffffff);
|
|
|
|
ev.data.l[1] = frame->sync_request_serial >> 32;
|
|
|
|
|
|
|
|
if (presentation_time != 0)
|
|
|
|
{
|
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
|
|
|
MetaCompositor *compositor = display->compositor;
|
|
|
|
int64_t presentation_time_server;
|
|
|
|
|
|
|
|
presentation_time_server =
|
|
|
|
meta_compositor_monotonic_to_high_res_xserver_time (compositor,
|
|
|
|
presentation_time);
|
2018-12-21 15:51:02 -05:00
|
|
|
int64_t presentation_time_offset = presentation_time_server - frame->frame_drawn_time;
|
|
|
|
if (presentation_time_offset == 0)
|
|
|
|
presentation_time_offset = 1;
|
|
|
|
|
|
|
|
if ((int32_t)presentation_time_offset == presentation_time_offset)
|
|
|
|
ev.data.l[2] = presentation_time_offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
ev.data.l[3] = refresh_interval;
|
|
|
|
ev.data.l[4] = 1000 * META_SYNC_DELAY;
|
|
|
|
|
|
|
|
meta_x11_error_trap_push (display->x11_display);
|
|
|
|
XSendEvent (xdisplay, ev.window, False, 0, (XEvent *) &ev);
|
|
|
|
XFlush (xdisplay);
|
|
|
|
meta_x11_error_trap_pop (display->x11_display);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
send_frame_timings (MetaWindowActorX11 *actor_x11,
|
|
|
|
FrameData *frame,
|
|
|
|
ClutterFrameInfo *frame_info,
|
|
|
|
int64_t presentation_time)
|
|
|
|
{
|
|
|
|
float refresh_rate;
|
|
|
|
int refresh_interval;
|
|
|
|
|
|
|
|
refresh_rate = frame_info->refresh_rate;
|
|
|
|
/* 0.0 is a flag for not known, but sanity-check against other odd numbers */
|
|
|
|
if (refresh_rate >= 1.0)
|
|
|
|
refresh_interval = (int) (0.5 + 1000000 / refresh_rate);
|
|
|
|
else
|
|
|
|
refresh_interval = 0;
|
|
|
|
|
|
|
|
do_send_frame_timings (actor_x11, frame, refresh_interval, presentation_time);
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
send_frame_messages_timeout (gpointer data)
|
|
|
|
{
|
|
|
|
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (data);
|
|
|
|
GList *l;
|
|
|
|
|
|
|
|
for (l = actor_x11->frames; l;)
|
|
|
|
{
|
|
|
|
GList *l_next = l->next;
|
|
|
|
FrameData *frame = l->data;
|
|
|
|
|
|
|
|
if (frame->frame_counter == -1)
|
|
|
|
{
|
|
|
|
do_send_frame_drawn (actor_x11, frame);
|
|
|
|
do_send_frame_timings (actor_x11, frame, 0, 0);
|
|
|
|
|
|
|
|
actor_x11->frames = g_list_delete_link (actor_x11->frames, l);
|
|
|
|
frame_data_free (frame);
|
|
|
|
}
|
|
|
|
|
|
|
|
l = l_next;
|
|
|
|
}
|
|
|
|
|
|
|
|
actor_x11->needs_frame_drawn = FALSE;
|
|
|
|
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;
|
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;
|
|
|
|
offset = MAX (0, actor_x11->frame_drawn_time + interval - current_time) / 1000;
|
|
|
|
|
|
|
|
/* 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);
|
2018-12-21 15:51:02 -05:00
|
|
|
GList *l;
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
|
|
for (l = actor_x11->frames; l; l = l->next)
|
|
|
|
{
|
|
|
|
FrameData *frame = l->data;
|
|
|
|
|
|
|
|
if (frame->frame_counter == -1)
|
|
|
|
frame->frame_counter = clutter_stage_get_frame_counter (stage);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
meta_window_actor_x11_frame_complete (MetaWindowActor *actor,
|
|
|
|
ClutterFrameInfo *frame_info,
|
|
|
|
int64_t presentation_time)
|
|
|
|
{
|
|
|
|
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor);
|
|
|
|
GList *l;
|
|
|
|
|
|
|
|
if (meta_window_actor_is_destroyed (actor))
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (l = actor_x11->frames; l;)
|
|
|
|
{
|
|
|
|
GList *l_next = l->next;
|
|
|
|
FrameData *frame = l->data;
|
|
|
|
int64_t frame_counter = frame_info->frame_counter;
|
|
|
|
|
|
|
|
if (frame->frame_counter != -1 && frame->frame_counter <= frame_counter)
|
|
|
|
{
|
|
|
|
MetaWindow *window =
|
|
|
|
meta_window_actor_get_meta_window (actor);
|
|
|
|
|
|
|
|
if (G_UNLIKELY (frame->frame_drawn_time == 0))
|
|
|
|
g_warning ("%s: Frame has assigned frame counter but no frame drawn time",
|
|
|
|
window->desc);
|
|
|
|
if (G_UNLIKELY (frame->frame_counter < frame_counter))
|
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_debug ("%s: frame_complete callback never occurred for frame %" G_GINT64_FORMAT,
|
|
|
|
window->desc, frame->frame_counter);
|
2018-12-21 15:51:02 -05:00
|
|
|
|
|
|
|
actor_x11->frames = g_list_delete_link (actor_x11->frames, l);
|
|
|
|
send_frame_timings (actor_x11, frame, frame_info, presentation_time);
|
|
|
|
frame_data_free (frame);
|
|
|
|
}
|
|
|
|
|
|
|
|
l = l_next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
MetaWindow *window =
|
|
|
|
meta_window_actor_get_meta_window (actor);
|
|
|
|
FrameData *frame;
|
|
|
|
|
|
|
|
if (meta_window_actor_is_destroyed (actor))
|
|
|
|
return;
|
|
|
|
|
|
|
|
frame = g_slice_new0 (FrameData);
|
|
|
|
frame->frame_counter = -1;
|
|
|
|
frame->sync_request_serial = window->sync_request_serial;
|
|
|
|
|
|
|
|
actor_x11->frames = g_list_prepend (actor_x11->frames, frame);
|
|
|
|
|
|
|
|
actor_x11->needs_frame_drawn = TRUE;
|
|
|
|
|
|
|
|
if (skip_sync_delay)
|
|
|
|
{
|
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 (actor_x11->frame_clock)
|
|
|
|
clutter_frame_clock_schedule_update_now (actor_x11->frame_clock);
|
|
|
|
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
|
2018-12-21 15:51:02 -05:00
|
|
|
* send a _NET_WM_FRAME_DRAWN. We do a 1-pixel redraw to get
|
|
|
|
* consistent timing with non-empty frames. If the window
|
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
|
|
|
* 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)
|
|
|
|
{
|
|
|
|
const cairo_rectangle_int_t clip = { 0, 0, 1, 1 };
|
|
|
|
clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (surface), &clip);
|
|
|
|
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;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Always put a shadow around windows with a frame - This should override
|
|
|
|
* the restriction about not putting a shadow around ARGB windows.
|
|
|
|
*/
|
|
|
|
if (meta_window_get_frame (window))
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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));
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2020-08-12 12:08:35 -04:00
|
|
|
actor_x11->frame_clock = clutter_actor_pick_frame_clock (actor, 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
|
|
|
if (actor_x11->frame_clock && actor_x11->pending_schedule_update_now)
|
|
|
|
{
|
|
|
|
clutter_frame_clock_schedule_update_now (actor_x11->frame_clock);
|
|
|
|
actor_x11->pending_schedule_update_now = FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
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;
|
|
|
|
|
|
|
|
/* 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.
|
|
|
|
*/
|
|
|
|
if (actor_x11->send_frame_messages_timer == 0 &&
|
|
|
|
actor_x11->needs_frame_drawn)
|
|
|
|
{
|
|
|
|
GList *l;
|
|
|
|
|
|
|
|
for (l = actor_x11->frames; l; l = l->next)
|
|
|
|
{
|
|
|
|
FrameData *frame = l->data;
|
|
|
|
|
|
|
|
if (frame->frame_drawn_time == 0)
|
|
|
|
do_send_frame_drawn (actor_x11, frame);
|
|
|
|
}
|
|
|
|
|
|
|
|
actor_x11->needs_frame_drawn = FALSE;
|
|
|
|
}
|
2019-12-02 08:01:30 -05:00
|
|
|
|
|
|
|
/* This is for Xwayland, and a no-op on plain Xorg */
|
|
|
|
window = meta_window_actor_get_meta_window (actor);
|
|
|
|
if (meta_window_x11_should_thaw_after_paint (window))
|
|
|
|
{
|
|
|
|
meta_window_x11_thaw_commits (window);
|
|
|
|
meta_window_x11_set_thaw_after_paint (window, FALSE);
|
|
|
|
}
|
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;
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
if (window->extended_sync_request_counter &&
|
|
|
|
!meta_window_updates_are_frozen (window))
|
2019-08-17 04:00:46 -04:00
|
|
|
meta_window_actor_queue_frame_drawn (actor, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2019-08-17 04:00:46 -04:00
|
|
|
cullable_parent_iface->cull_out (cullable, unobscured_region, clip_region);
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
cullable_parent_iface->reset_culling (cullable);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
cullable_iface_init (MetaCullableInterface *iface)
|
|
|
|
{
|
|
|
|
cullable_parent_iface = g_type_interface_peek_parent (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);
|
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (object);
|
|
|
|
|
|
|
|
g_list_free_full (actor_x11->frames, (GDestroyNotify) frame_data_free);
|
|
|
|
|
|
|
|
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;
|
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;
|
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
|
|
|
}
|