From d1f670bc1aacfd2fc4d4451b64ab9b6cf4af575c Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Tue, 5 Oct 2021 12:02:13 -0400 Subject: [PATCH] import mutter-3.32.2-59.el8 --- .../0001-clutter-Backport-of-touch-mode.patch | 357 +++ SOURCES/geometric-picking.patch | 2091 +++++++++++++++++ SOURCES/wayland-frame-callback-rework.patch | 1045 ++++++++ SOURCES/xwayland-xauth-xhost-user.patch | 479 ++++ SPECS/mutter.spec | 30 +- 5 files changed, 4001 insertions(+), 1 deletion(-) create mode 100644 SOURCES/0001-clutter-Backport-of-touch-mode.patch create mode 100644 SOURCES/geometric-picking.patch create mode 100644 SOURCES/wayland-frame-callback-rework.patch create mode 100644 SOURCES/xwayland-xauth-xhost-user.patch diff --git a/SOURCES/0001-clutter-Backport-of-touch-mode.patch b/SOURCES/0001-clutter-Backport-of-touch-mode.patch new file mode 100644 index 0000000..dff32fa --- /dev/null +++ b/SOURCES/0001-clutter-Backport-of-touch-mode.patch @@ -0,0 +1,357 @@ +From 2a2e870c139e2130b00d582546616269bca27458 Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Fri, 4 Sep 2020 17:11:36 +0200 +Subject: [PATCH] clutter: Backport of ::touch-mode + +In upstream/master this is a ClutterSeat readonly property. Add it to +ClutterDeviceManager here, the mechanism and triggering is the same +though. +--- + clutter/clutter/clutter-device-manager.c | 24 +++ + clutter/clutter/clutter-device-manager.h | 2 + + .../evdev/clutter-device-manager-evdev.c | 179 ++++++++++++++++++ + 3 files changed, 205 insertions(+) + +diff --git a/clutter/clutter/clutter-device-manager.c b/clutter/clutter/clutter-device-manager.c +index c676384..e1cc455 100644 +--- a/clutter/clutter/clutter-device-manager.c ++++ b/clutter/clutter/clutter-device-manager.c +@@ -62,6 +62,7 @@ enum + PROP_0, + + PROP_BACKEND, ++ PROP_TOUCH_MODE, + + PROP_LAST + }; +@@ -108,6 +109,7 @@ clutter_device_manager_set_property (GObject *gobject, + priv->backend = g_value_get_object (value); + break; + ++ case PROP_TOUCH_MODE: + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + } +@@ -127,6 +129,10 @@ clutter_device_manager_get_property (GObject *gobject, + g_value_set_object (value, priv->backend); + break; + ++ case PROP_TOUCH_MODE: ++ g_value_set_boolean (value, FALSE); ++ break; ++ + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); + } +@@ -143,6 +149,12 @@ clutter_device_manager_class_init (ClutterDeviceManagerClass *klass) + P_("The ClutterBackend of the device manager"), + CLUTTER_TYPE_BACKEND, + CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); ++ obj_props[PROP_TOUCH_MODE] = ++ g_param_spec_boolean ("touch-mode", ++ P_("Touch mode"), ++ P_("Touch mode"), ++ FALSE, ++ CLUTTER_PARAM_READABLE); + + gobject_class->set_property = clutter_device_manager_set_property; + gobject_class->get_property = clutter_device_manager_get_property; +@@ -579,3 +591,15 @@ clutter_device_manager_get_kbd_a11y_settings (ClutterDeviceManager *device_man + + *settings = device_manager->priv->kbd_a11y_settings; + } ++ ++gboolean ++clutter_device_manager_get_touch_mode (ClutterDeviceManager *device_manager) ++{ ++ gboolean touch_mode; ++ ++ g_return_val_if_fail (CLUTTER_IS_DEVICE_MANAGER (device_manager), FALSE); ++ ++ g_object_get (G_OBJECT (device_manager), "touch-mode", &touch_mode, NULL); ++ ++ return touch_mode; ++} +diff --git a/clutter/clutter/clutter-device-manager.h b/clutter/clutter/clutter-device-manager.h +index 1cbf030..a4a6271 100644 +--- a/clutter/clutter/clutter-device-manager.h ++++ b/clutter/clutter/clutter-device-manager.h +@@ -155,6 +155,8 @@ void clutter_device_manager_set_kbd_a11y_settings (ClutterDeviceManager *devic + CLUTTER_EXPORT + void clutter_device_manager_get_kbd_a11y_settings (ClutterDeviceManager *device_manager, + ClutterKbdA11ySettings *settings); ++CLUTTER_EXPORT ++gboolean clutter_device_manager_get_touch_mode (ClutterDeviceManager *device_manager); + + G_END_DECLS + +diff --git a/clutter/clutter/evdev/clutter-device-manager-evdev.c b/clutter/clutter/evdev/clutter-device-manager-evdev.c +index 84b0aad..78b5b64 100644 +--- a/clutter/clutter/evdev/clutter-device-manager-evdev.c ++++ b/clutter/clutter/evdev/clutter-device-manager-evdev.c +@@ -108,6 +108,19 @@ struct _ClutterDeviceManagerEvdevPrivate + + gint device_id_next; + GList *free_device_ids; ++ ++ guint tablet_mode_switch_state : 1; ++ guint has_touchscreen : 1; ++ guint has_tablet_switch : 1; ++ guint has_pointer : 1; ++ guint touch_mode : 1; ++}; ++ ++enum ++{ ++ PROP_0, ++ PROP_TOUCH_MODE, ++ N_PROPS + }; + + static void clutter_device_manager_evdev_event_extender_init (ClutterEventExtenderInterface *iface); +@@ -765,6 +778,34 @@ clutter_event_source_free (ClutterEventSource *source) + g_source_unref (g_source); + } + ++static void ++update_touch_mode (ClutterDeviceManagerEvdev *manager_evdev) ++{ ++ ClutterDeviceManagerEvdevPrivate *priv = manager_evdev->priv; ++ gboolean touch_mode; ++ ++ /* No touch mode if we don't have a touchscreen, easy */ ++ if (!priv->has_touchscreen) ++ touch_mode = FALSE; ++ /* If we have a tablet mode switch, honor it being unset */ ++ else if (priv->has_tablet_switch && !priv->tablet_mode_switch_state) ++ touch_mode = FALSE; ++ /* If tablet mode is enabled, go for it */ ++ else if (priv->has_tablet_switch && priv->tablet_mode_switch_state) ++ touch_mode = TRUE; ++ /* If there is no tablet mode switch (eg. kiosk machines), ++ * assume touch-mode is mutually exclusive with pointers. ++ */ ++ else ++ touch_mode = !priv->has_pointer; ++ ++ if (priv->touch_mode != touch_mode) ++ { ++ priv->touch_mode = touch_mode; ++ g_object_notify (G_OBJECT (manager_evdev), "touch-mode"); ++ } ++} ++ + static void + evdev_add_device (ClutterDeviceManagerEvdev *manager_evdev, + struct libinput_device *libinput_device) +@@ -942,19 +983,81 @@ flush_event_queue (void) + } + } + ++static gboolean ++has_touchscreen (ClutterDeviceManagerEvdev *manager_evdev) ++{ ++ ClutterDeviceManagerEvdevPrivate *priv = manager_evdev->priv; ++ GSList *l; ++ ++ for (l = priv->devices; l; l = l->next) ++ { ++ ClutterInputDeviceType device_type; ++ ++ device_type = clutter_input_device_get_device_type (l->data); ++ ++ if (device_type == CLUTTER_TOUCHSCREEN_DEVICE) ++ return TRUE; ++ } ++ ++ return FALSE; ++} ++ ++static gboolean ++device_type_is_pointer (ClutterInputDeviceType device_type) ++{ ++ return (device_type == CLUTTER_POINTER_DEVICE || ++ device_type == CLUTTER_TOUCHPAD_DEVICE); ++} ++ ++static gboolean ++has_pointer (ClutterDeviceManagerEvdev *manager_evdev) ++{ ++ ClutterDeviceManagerEvdevPrivate *priv = manager_evdev->priv; ++ GSList *l; ++ ++ for (l = priv->devices; l; l = l->next) ++ { ++ ClutterInputDeviceType device_type; ++ ++ device_type = clutter_input_device_get_device_type (l->data); ++ ++ if (device_type_is_pointer (device_type)) ++ return TRUE; ++ } ++ ++ return FALSE; ++} ++ + static gboolean + process_base_event (ClutterDeviceManagerEvdev *manager_evdev, + struct libinput_event *event) + { ++ ClutterDeviceManagerEvdevPrivate *priv = manager_evdev->priv; + ClutterInputDevice *device; + struct libinput_device *libinput_device; + gboolean handled = TRUE; ++ gboolean check_touch_mode; + + switch (libinput_event_get_type (event)) + { + case LIBINPUT_EVENT_DEVICE_ADDED: + libinput_device = libinput_event_get_device (event); + ++ priv->has_touchscreen |= ++ libinput_device_has_capability (libinput_device, LIBINPUT_DEVICE_CAP_TOUCH); ++ priv->has_pointer |= ++ libinput_device_has_capability (libinput_device, LIBINPUT_DEVICE_CAP_POINTER); ++ check_touch_mode = priv->has_touchscreen | priv->has_pointer; ++ ++ if (libinput_device_has_capability (libinput_device, ++ LIBINPUT_DEVICE_CAP_SWITCH) && ++ libinput_device_switch_has_switch (libinput_device, ++ LIBINPUT_SWITCH_TABLET_MODE)) ++ { ++ priv->has_tablet_switch = TRUE; ++ check_touch_mode = TRUE; ++ } ++ + evdev_add_device (manager_evdev, libinput_device); + break; + +@@ -966,7 +1069,17 @@ process_base_event (ClutterDeviceManagerEvdev *manager_evdev, + + libinput_device = libinput_event_get_device (event); + ++ check_touch_mode = ++ libinput_device_has_capability (libinput_device, LIBINPUT_DEVICE_CAP_TOUCH); + device = libinput_device_get_user_data (libinput_device); ++ if (check_touch_mode) ++ priv->has_touchscreen = has_touchscreen (manager_evdev); ++ if (device_type_is_pointer (clutter_input_device_get_device_type (device))) ++ { ++ priv->has_pointer = has_pointer (manager_evdev); ++ check_touch_mode = TRUE; ++ } ++ + evdev_remove_device (manager_evdev, + CLUTTER_INPUT_DEVICE_EVDEV (device)); + break; +@@ -975,6 +1088,9 @@ process_base_event (ClutterDeviceManagerEvdev *manager_evdev, + handled = FALSE; + } + ++ if (check_touch_mode) ++ update_touch_mode (manager_evdev); ++ + return handled; + } + +@@ -1752,6 +1868,23 @@ process_device_event (ClutterDeviceManagerEvdev *manager_evdev, + notify_pad_ring (device, time, number, source, group, mode, angle); + break; + } ++ case LIBINPUT_EVENT_SWITCH_TOGGLE: ++ { ++ ClutterDeviceManagerEvdevPrivate *priv = manager_evdev->priv; ++ struct libinput_event_switch *switch_event = ++ libinput_event_get_switch_event (event); ++ enum libinput_switch sw = ++ libinput_event_switch_get_switch (switch_event); ++ enum libinput_switch_state state = ++ libinput_event_switch_get_switch_state (switch_event); ++ ++ if (sw == LIBINPUT_SWITCH_TABLET_MODE) ++ { ++ priv->tablet_mode_switch_state = (state == LIBINPUT_SWITCH_STATE_ON); ++ update_touch_mode (manager_evdev); ++ } ++ break; ++ } + default: + handled = FALSE; + } +@@ -1967,6 +2100,10 @@ clutter_device_manager_evdev_constructed (GObject *gobject) + + source = clutter_event_source_new (manager_evdev); + priv->event_source = source; ++ ++ priv->has_touchscreen = has_touchscreen (manager_evdev); ++ priv->has_pointer = has_pointer (manager_evdev); ++ update_touch_mode (manager_evdev); + } + + static void +@@ -2001,6 +2138,43 @@ clutter_device_manager_evdev_dispose (GObject *object) + G_OBJECT_CLASS (clutter_device_manager_evdev_parent_class)->dispose (object); + } + ++static void ++clutter_device_manager_evdev_set_property (GObject *object, ++ guint prop_id, ++ const GValue *value, ++ GParamSpec *pspec) ++{ ++ switch (prop_id) ++ { ++ case PROP_TOUCH_MODE: ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ } ++} ++ ++static void ++clutter_device_manager_evdev_get_property (GObject *object, ++ guint prop_id, ++ GValue *value, ++ GParamSpec *pspec) ++{ ++ ClutterDeviceManagerEvdev *manager_evdev; ++ ClutterDeviceManagerEvdevPrivate *priv; ++ ++ manager_evdev = CLUTTER_DEVICE_MANAGER_EVDEV (object); ++ priv = manager_evdev->priv; ++ ++ switch (prop_id) ++ { ++ case PROP_TOUCH_MODE: ++ g_value_set_boolean (value, priv->touch_mode); ++ break; ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ } ++} ++ ++ + static void + clutter_device_manager_evdev_finalize (GObject *object) + { +@@ -2036,6 +2210,8 @@ clutter_device_manager_evdev_class_init (ClutterDeviceManagerEvdevClass *klass) + gobject_class->constructed = clutter_device_manager_evdev_constructed; + gobject_class->finalize = clutter_device_manager_evdev_finalize; + gobject_class->dispose = clutter_device_manager_evdev_dispose; ++ gobject_class->set_property = clutter_device_manager_evdev_set_property; ++ gobject_class->get_property = clutter_device_manager_evdev_get_property; + + manager_class = CLUTTER_DEVICE_MANAGER_CLASS (klass); + manager_class->add_device = clutter_device_manager_evdev_add_device; +@@ -2047,6 +2223,9 @@ clutter_device_manager_evdev_class_init (ClutterDeviceManagerEvdevClass *klass) + manager_class->get_supported_virtual_device_types = clutter_device_manager_evdev_get_supported_virtual_device_types; + manager_class->compress_motion = clutter_device_manager_evdev_compress_motion; + manager_class->apply_kbd_a11y_settings = clutter_device_manager_evdev_apply_kbd_a11y_settings; ++ ++ g_object_class_override_property (gobject_class, PROP_TOUCH_MODE, ++ "touch-mode"); + } + + static void +-- +2.29.2 + diff --git a/SOURCES/geometric-picking.patch b/SOURCES/geometric-picking.patch new file mode 100644 index 0000000..6892dca --- /dev/null +++ b/SOURCES/geometric-picking.patch @@ -0,0 +1,2091 @@ +From ac946bf95ce3e4dc900f72dcb4189dd49bdb3155 Mon Sep 17 00:00:00 2001 +From: Daniel van Vugt +Date: Thu, 18 Jul 2019 16:56:41 +0800 +Subject: [PATCH 1/4] clutter/point: Add ClutterPoint quarilateral testing API + +Add a function to check whether a point is inside a quadrilateral +by checking the cross product of vectors with the quadrilateral +points, and the point being checked. + +If the passed quadrilateral is zero-sized, no point is ever reported +to be inside it. + +This will be used by the next commit when comparing the transformed +actor vertices. + +[feaneron: add a commit message and remove unecessary code] + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/189 +--- + clutter/clutter/clutter-base-types.c | 62 ++++++++++++++++++++++++++++ + clutter/clutter/clutter-types.h | 3 ++ + 2 files changed, 65 insertions(+) + +diff --git a/clutter/clutter/clutter-base-types.c b/clutter/clutter/clutter-base-types.c +index aeb25c90ef..c84f9aa64b 100644 +--- a/clutter/clutter/clutter-base-types.c ++++ b/clutter/clutter/clutter-base-types.c +@@ -570,6 +570,68 @@ G_DEFINE_BOXED_TYPE_WITH_CODE (ClutterPoint, clutter_point, + clutter_point_free, + CLUTTER_REGISTER_INTERVAL_PROGRESS (clutter_point_progress)) + ++static int ++clutter_point_compare_line (const ClutterPoint *p, ++ const ClutterPoint *a, ++ const ClutterPoint *b) ++{ ++ float x1 = b->x - a->x; ++ float y1 = b->y - a->y; ++ float x2 = p->x - a->x; ++ float y2 = p->y - a->y; ++ float cross_z = x1 * y2 - y1 * x2; ++ ++ if (cross_z > 0.f) ++ return 1; ++ else if (cross_z < 0.f) ++ return -1; ++ else ++ return 0; ++} ++ ++/** ++ * clutter_point_inside_quadrilateral: ++ * @point: a #ClutterPoint to test ++ * @vertices: array of vertices of the quadrilateral, in clockwise order, ++ * from top-left to bottom-left ++ * ++ * Determines whether a point is inside the convex quadrilateral provided, ++ * and not on any of its edges or vertices. ++ * ++ * Returns: %TRUE if @point is inside the quadrilateral ++ */ ++gboolean ++clutter_point_inside_quadrilateral (const ClutterPoint *point, ++ const ClutterPoint *vertices) ++{ ++ unsigned int i; ++ int first_side; ++ ++ first_side = 0; ++ ++ for (i = 0; i < 4; i++) ++ { ++ int side; ++ ++ side = clutter_point_compare_line (point, ++ &vertices[i], ++ &vertices[(i + 1) % 4]); ++ ++ if (side) ++ { ++ if (first_side == 0) ++ first_side = side; ++ else if (side != first_side) ++ return FALSE; ++ } ++ } ++ ++ if (first_side == 0) ++ return FALSE; ++ ++ return TRUE; ++} ++ + + + /* +diff --git a/clutter/clutter/clutter-types.h b/clutter/clutter/clutter-types.h +index 0f0fb1c2ac..0508028273 100644 +--- a/clutter/clutter/clutter-types.h ++++ b/clutter/clutter/clutter-types.h +@@ -200,6 +200,9 @@ float clutter_point_distance (const ClutterPoint *a, + const ClutterPoint *b, + float *x_distance, + float *y_distance); ++CLUTTER_EXPORT ++gboolean clutter_point_inside_quadrilateral (const ClutterPoint *point, ++ const ClutterPoint *vertices); + + /** + * ClutterSize: +-- +2.29.2 + + +From 8abac81711cfef8317bb675349d6b5b0a79eb05d Mon Sep 17 00:00:00 2001 +From: Daniel van Vugt +Date: Thu, 2 Aug 2018 19:03:30 +0800 +Subject: [PATCH 2/4] clutter: Introduce geometric picking + +Currently, Clutter does picking by drawing with Cogl and reading +the pixel that's beneath the given point. Since Cogl has a journal +that records drawing operations, and has optimizations to read a +single pixel from a list of rectangle, it would be expected that +we would hit this fast path and not flush the journal while picking. + +However, that's not the case: dithering, clipping with scissors, etc, +can all flush the journal, issuing commands to the GPU and making +picking slow. On NVidia-based systems, this glReadPixels() call is +extremely costly. + +Introduce geometric picking, and avoid using the Cogl journal entirely. +Do this by introducing a stack of actors in ClutterStage. This stack +is cached, but for now, don't use the cache as much as possible. + +The picking routines are still tied to painting. + +When projecting the actor vertexes, do it manually and take the modelview +matrix of the framebuffer into account as well. + +CPU usage on an Intel i7-7700, tested with two different GPUs/drivers: + + | | Intel | Nvidia | + | ------: | --------: | -----: | + | Moving the mouse: | + | Before | 10% | 10% | + | After | 6% | 6% | + | Moving a window: | + | Before | 23% | 81% | + | After | 19% | 40% | + +Closes: https://gitlab.gnome.org/GNOME/mutter/issues/154, + https://gitlab.gnome.org/GNOME/mutter/issues/691 + +Helps significantly with: https://gitlab.gnome.org/GNOME/mutter/issues/283, + https://gitlab.gnome.org/GNOME/mutter/issues/590, + https://gitlab.gnome.org/GNOME/mutter/issues/700 + +v2: Fix code style issues + Simplify quadrilateral checks + Remove the 0.5f hack + Differentiate axis-aligned rectangles + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/189 +--- + clutter/clutter/clutter-actor-private.h | 2 - + clutter/clutter/clutter-actor.c | 232 ++++++---- + clutter/clutter/clutter-actor.h | 4 + + clutter/clutter/clutter-debug.h | 1 - + clutter/clutter/clutter-main.c | 120 ----- + clutter/clutter/clutter-private.h | 5 - + clutter/clutter/clutter-stage-private.h | 16 +- + clutter/clutter/clutter-stage-window.c | 18 - + clutter/clutter/clutter-stage-window.h | 8 - + clutter/clutter/clutter-stage.c | 459 +++++++++++-------- + clutter/clutter/cogl/clutter-stage-cogl.c | 52 --- + clutter/clutter/deprecated/clutter-texture.c | 93 +--- + clutter/tests/conform/actor-pick.c | 99 +--- + clutter/tests/conform/meson.build | 1 - + clutter/tests/conform/texture.c | 84 ---- + src/compositor/meta-surface-actor.c | 27 +- + 16 files changed, 439 insertions(+), 782 deletions(-) + delete mode 100644 clutter/tests/conform/texture.c + +diff --git a/clutter/clutter/clutter-actor-private.h b/clutter/clutter/clutter-actor-private.h +index c44f6342fd..9bf1a30493 100644 +--- a/clutter/clutter/clutter-actor-private.h ++++ b/clutter/clutter/clutter-actor-private.h +@@ -297,8 +297,6 @@ const gchar * _clutter_actor_get_debug_name + void _clutter_actor_push_clone_paint (void); + void _clutter_actor_pop_clone_paint (void); + +-guint32 _clutter_actor_get_pick_id (ClutterActor *self); +- + void _clutter_actor_shader_pre_paint (ClutterActor *actor, + gboolean repeat); + void _clutter_actor_shader_post_paint (ClutterActor *actor); +diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c +index 43093fe79d..01ffa51caa 100644 +--- a/clutter/clutter/clutter-actor.c ++++ b/clutter/clutter/clutter-actor.c +@@ -730,8 +730,6 @@ struct _ClutterActorPrivate + + gchar *name; /* a non-unique name, used for debugging */ + +- gint32 pick_id; /* per-stage unique id, used for picking */ +- + /* a back-pointer to the Pango context that we can use + * to create pre-configured PangoLayout + */ +@@ -1281,6 +1279,105 @@ clutter_actor_verify_map_state (ClutterActor *self) + + #endif /* CLUTTER_ENABLE_DEBUG */ + ++static gboolean ++_clutter_actor_transform_local_box_to_stage (ClutterActor *self, ++ ClutterStage *stage, ++ const ClutterActorBox *box, ++ ClutterPoint vertices[4]) ++{ ++ CoglFramebuffer *fb = cogl_get_draw_framebuffer (); ++ CoglMatrix stage_transform, inv_stage_transform; ++ CoglMatrix modelview, transform_to_stage; ++ int v; ++ ++ clutter_actor_get_transform (CLUTTER_ACTOR (stage), &stage_transform); ++ if (!cogl_matrix_get_inverse (&stage_transform, &inv_stage_transform)) ++ return FALSE; ++ cogl_framebuffer_get_modelview_matrix (fb, &modelview); ++ cogl_matrix_multiply (&transform_to_stage, &inv_stage_transform, &modelview); ++ ++ vertices[0].x = box->x1; ++ vertices[0].y = box->y1; ++ ++ vertices[1].x = box->x2; ++ vertices[1].y = box->y1; ++ ++ vertices[2].x = box->x2; ++ vertices[2].y = box->y2; ++ ++ vertices[3].x = box->x1; ++ vertices[3].y = box->y2; ++ ++ for (v = 0; v < 4; v++) ++ { ++ float z = 0.f; ++ float w = 1.f; ++ ++ cogl_matrix_transform_point (&transform_to_stage, ++ &vertices[v].x, ++ &vertices[v].y, ++ &z, ++ &w); ++ } ++ ++ return TRUE; ++} ++ ++/** ++ * clutter_actor_pick_box: ++ * @self: The #ClutterActor being "pick" painted. ++ * @box: A rectangle in the actor's own local coordinates. ++ * ++ * Logs (does a virtual paint of) a rectangle for picking. Note that @box is ++ * in the actor's own local coordinates, so is usually {0,0,width,height} ++ * to include the whole actor. That is unless the actor has a shaped input ++ * region in which case you may wish to log the (multiple) smaller rectangles ++ * that make up the input region. ++ */ ++void ++clutter_actor_pick_box (ClutterActor *self, ++ const ClutterActorBox *box) ++{ ++ ClutterStage *stage; ++ ClutterPoint vertices[4]; ++ ++ g_return_if_fail (CLUTTER_IS_ACTOR (self)); ++ g_return_if_fail (box != NULL); ++ ++ if (box->x1 >= box->x2 || box->y1 >= box->y2) ++ return; ++ ++ stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self)); ++ ++ if (_clutter_actor_transform_local_box_to_stage (self, stage, box, vertices)) ++ clutter_stage_log_pick (stage, vertices, self); ++} ++ ++static gboolean ++_clutter_actor_push_pick_clip (ClutterActor *self, ++ const ClutterActorBox *clip) ++{ ++ ClutterStage *stage; ++ ClutterPoint vertices[4]; ++ ++ stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self)); ++ ++ if (!_clutter_actor_transform_local_box_to_stage (self, stage, clip, vertices)) ++ return FALSE; ++ ++ clutter_stage_push_pick_clip (stage, vertices); ++ return TRUE; ++} ++ ++static void ++_clutter_actor_pop_pick_clip (ClutterActor *self) ++{ ++ ClutterActor *stage; ++ ++ stage = _clutter_actor_get_stage_internal (self); ++ clutter_stage_pop_pick_clip (CLUTTER_STAGE (stage)); ++} ++ + static void + clutter_actor_set_mapped (ClutterActor *self, + gboolean mapped) +@@ -1509,8 +1606,7 @@ clutter_actor_update_map_state (ClutterActor *self, + static void + clutter_actor_real_map (ClutterActor *self) + { +- ClutterActorPrivate *priv = self->priv; +- ClutterActor *stage, *iter; ++ ClutterActor *iter; + + g_assert (!CLUTTER_ACTOR_IS_MAPPED (self)); + +@@ -1521,13 +1617,6 @@ clutter_actor_real_map (ClutterActor *self) + + self->priv->needs_paint_volume_update = TRUE; + +- stage = _clutter_actor_get_stage_internal (self); +- priv->pick_id = _clutter_stage_acquire_pick_id (CLUTTER_STAGE (stage), self); +- +- CLUTTER_NOTE (ACTOR, "Pick id '%d' for actor '%s'", +- priv->pick_id, +- _clutter_actor_get_debug_name (self)); +- + clutter_actor_ensure_resource_scale (self); + + /* notify on parent mapped before potentially mapping +@@ -1632,11 +1721,6 @@ clutter_actor_real_unmap (ClutterActor *self) + + stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self)); + +- if (stage != NULL) +- _clutter_stage_release_pick_id (stage, priv->pick_id); +- +- priv->pick_id = -1; +- + if (stage != NULL && + clutter_stage_get_key_focus (stage) == self) + { +@@ -2255,46 +2339,16 @@ static void + clutter_actor_real_pick (ClutterActor *self, + const ClutterColor *color) + { +- CoglFramebuffer *framebuffer = cogl_get_draw_framebuffer (); +- +- /* the default implementation is just to paint a rectangle +- * with the same size of the actor using the passed color +- */ + if (clutter_actor_should_pick_paint (self)) + { +- static CoglPipeline *default_pick_pipeline = NULL; +- ClutterActorBox box = { 0, }; +- CoglPipeline *pick_pipeline; +- float width, height; +- +- if (G_UNLIKELY (default_pick_pipeline == NULL)) +- { +- CoglContext *ctx = +- clutter_backend_get_cogl_context (clutter_get_default_backend ()); +- +- default_pick_pipeline = cogl_pipeline_new (ctx); +- } +- +- g_assert (default_pick_pipeline != NULL); +- pick_pipeline = cogl_pipeline_copy (default_pick_pipeline); ++ ClutterActorBox box = { ++ .x1 = 0, ++ .y1 = 0, ++ .x2 = clutter_actor_get_width (self), ++ .y2 = clutter_actor_get_height (self), ++ }; + +- clutter_actor_get_allocation_box (self, &box); +- +- width = box.x2 - box.x1; +- height = box.y2 - box.y1; +- +- cogl_pipeline_set_color4ub (pick_pipeline, +- color->red, +- color->green, +- color->blue, +- color->alpha); +- +- cogl_framebuffer_draw_rectangle (framebuffer, +- pick_pipeline, +- 0, 0, +- width, height); +- +- cogl_object_unref (pick_pipeline); ++ clutter_actor_pick_box (self, &box); + } + + /* XXX - this thoroughly sucks, but we need to maintain compatibility +@@ -3585,15 +3639,6 @@ _clutter_actor_update_last_paint_volume (ClutterActor *self) + priv->last_paint_volume_valid = TRUE; + } + +-guint32 +-_clutter_actor_get_pick_id (ClutterActor *self) +-{ +- if (self->priv->pick_id < 0) +- return 0; +- +- return self->priv->pick_id; +-} +- + /* This is the same as clutter_actor_add_effect except that it doesn't + queue a redraw and it doesn't notify on the effect property */ + static void +@@ -3826,6 +3871,7 @@ clutter_actor_paint (ClutterActor *self) + ClutterActorPrivate *priv; + ClutterPickMode pick_mode; + gboolean culling_inhibited; ++ ClutterActorBox clip; + gboolean clip_set = FALSE; + ClutterStage *stage; + +@@ -3919,24 +3965,38 @@ clutter_actor_paint (ClutterActor *self) + + if (priv->has_clip) + { +- CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage); +- cogl_framebuffer_push_rectangle_clip (fb, +- priv->clip.origin.x, +- priv->clip.origin.y, +- priv->clip.origin.x + priv->clip.size.width, +- priv->clip.origin.y + priv->clip.size.height); ++ clip.x1 = priv->clip.origin.x; ++ clip.y1 = priv->clip.origin.y; ++ clip.x2 = priv->clip.origin.x + priv->clip.size.width; ++ clip.y2 = priv->clip.origin.y + priv->clip.size.height; + clip_set = TRUE; + } + else if (priv->clip_to_allocation) + { +- CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage); +- gfloat width, height; ++ clip.x1 = 0.f; ++ clip.y1 = 0.f; ++ clip.x2 = priv->allocation.x2 - priv->allocation.x1; ++ clip.y2 = priv->allocation.y2 - priv->allocation.y1; ++ clip_set = TRUE; ++ } + +- width = priv->allocation.x2 - priv->allocation.x1; +- height = priv->allocation.y2 - priv->allocation.y1; ++ if (clip_set) ++ { ++ if (pick_mode == CLUTTER_PICK_NONE) ++ { ++ CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage); + +- cogl_framebuffer_push_rectangle_clip (fb, 0, 0, width, height); +- clip_set = TRUE; ++ cogl_framebuffer_push_rectangle_clip (fb, ++ clip.x1, ++ clip.y1, ++ clip.x2, ++ clip.y2); ++ } ++ else ++ { ++ if (!_clutter_actor_push_pick_clip (self, &clip)) ++ clip_set = FALSE; ++ } + } + + if (pick_mode == CLUTTER_PICK_NONE) +@@ -4020,9 +4080,16 @@ clutter_actor_paint (ClutterActor *self) + done: + if (clip_set) + { +- CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage); ++ if (pick_mode == CLUTTER_PICK_NONE) ++ { ++ CoglFramebuffer *fb = _clutter_stage_get_active_framebuffer (stage); + +- cogl_framebuffer_pop_clip (fb); ++ cogl_framebuffer_pop_clip (fb); ++ } ++ else ++ { ++ _clutter_actor_pop_pick_clip (self); ++ } + } + + cogl_pop_matrix (); +@@ -4093,11 +4160,12 @@ clutter_actor_continue_paint (ClutterActor *self) + { + ClutterColor col = { 0, }; + +- _clutter_id_to_color (_clutter_actor_get_pick_id (self), &col); +- +- /* Actor will then paint silhouette of itself in supplied +- * color. See clutter_stage_get_actor_at_pos() for where +- * picking is enabled. ++ /* The actor will log a silhouette of itself to the stage pick log. ++ * Note that the picking color is no longer used as the "log" instead ++ * keeps a weak pointer to the actor itself. But we keep the color ++ * parameter for now so as to maintain ABI compatibility. The color ++ * parameter can be removed when someone feels like breaking the ABI ++ * along with gnome-shell. + * + * XXX:2.0 - Call the pick() virtual directly + */ +@@ -8603,8 +8671,6 @@ clutter_actor_init (ClutterActor *self) + + self->priv = priv = clutter_actor_get_instance_private (self); + +- priv->pick_id = -1; +- + priv->opacity = 0xff; + priv->show_on_set_parent = TRUE; + priv->resource_scale = -1.0f; +diff --git a/clutter/clutter/clutter-actor.h b/clutter/clutter/clutter-actor.h +index 3e7a59ac0c..16b438ba64 100644 +--- a/clutter/clutter/clutter-actor.h ++++ b/clutter/clutter/clutter-actor.h +@@ -910,6 +910,10 @@ void clutter_actor_bind_model_with_properties + const char *first_model_property, + ...); + ++CLUTTER_EXPORT ++void clutter_actor_pick_box (ClutterActor *self, ++ const ClutterActorBox *box); ++ + G_END_DECLS + + #endif /* __CLUTTER_ACTOR_H__ */ +diff --git a/clutter/clutter/clutter-debug.h b/clutter/clutter/clutter-debug.h +index 2462385f65..7d170d2d54 100644 +--- a/clutter/clutter/clutter-debug.h ++++ b/clutter/clutter/clutter-debug.h +@@ -30,7 +30,6 @@ typedef enum + typedef enum + { + CLUTTER_DEBUG_NOP_PICKING = 1 << 0, +- CLUTTER_DEBUG_DUMP_PICK_BUFFERS = 1 << 1 + } ClutterPickDebugFlag; + + typedef enum +diff --git a/clutter/clutter/clutter-main.c b/clutter/clutter/clutter-main.c +index 645c8bceb6..11c221a65b 100644 +--- a/clutter/clutter/clutter-main.c ++++ b/clutter/clutter/clutter-main.c +@@ -131,7 +131,6 @@ static const GDebugKey clutter_debug_keys[] = { + + static const GDebugKey clutter_pick_debug_keys[] = { + { "nop-picking", CLUTTER_DEBUG_NOP_PICKING }, +- { "dump-pick-buffers", CLUTTER_DEBUG_DUMP_PICK_BUFFERS }, + }; + + static const GDebugKey clutter_paint_debug_keys[] = { +@@ -533,125 +532,6 @@ clutter_get_motion_events_enabled (void) + return _clutter_context_get_motion_events_enabled (); + } + +-void +-_clutter_id_to_color (guint id_, +- ClutterColor *col) +-{ +- ClutterMainContext *ctx; +- gint red, green, blue; +- +- ctx = _clutter_context_get_default (); +- +- if (ctx->fb_g_mask == 0) +- { +- /* Figure out framebuffer masks used for pick */ +- cogl_get_bitmasks (&ctx->fb_r_mask, +- &ctx->fb_g_mask, +- &ctx->fb_b_mask, NULL); +- +- ctx->fb_r_mask_used = ctx->fb_r_mask; +- ctx->fb_g_mask_used = ctx->fb_g_mask; +- ctx->fb_b_mask_used = ctx->fb_b_mask; +- +- /* XXX - describe what "fuzzy picking" is */ +- if (clutter_use_fuzzy_picking) +- { +- ctx->fb_r_mask_used--; +- ctx->fb_g_mask_used--; +- ctx->fb_b_mask_used--; +- } +- } +- +- /* compute the numbers we'll store in the components */ +- red = (id_ >> (ctx->fb_g_mask_used+ctx->fb_b_mask_used)) +- & (0xff >> (8-ctx->fb_r_mask_used)); +- green = (id_ >> ctx->fb_b_mask_used) +- & (0xff >> (8-ctx->fb_g_mask_used)); +- blue = (id_) +- & (0xff >> (8-ctx->fb_b_mask_used)); +- +- /* shift left bits a bit and add one, this circumvents +- * at least some potential rounding errors in GL/GLES +- * driver / hw implementation. +- */ +- if (ctx->fb_r_mask_used != ctx->fb_r_mask) +- red = red * 2; +- if (ctx->fb_g_mask_used != ctx->fb_g_mask) +- green = green * 2; +- if (ctx->fb_b_mask_used != ctx->fb_b_mask) +- blue = blue * 2; +- +- /* shift up to be full 8bit values */ +- red = (red << (8 - ctx->fb_r_mask)) | (0x7f >> (ctx->fb_r_mask_used)); +- green = (green << (8 - ctx->fb_g_mask)) | (0x7f >> (ctx->fb_g_mask_used)); +- blue = (blue << (8 - ctx->fb_b_mask)) | (0x7f >> (ctx->fb_b_mask_used)); +- +- col->red = red; +- col->green = green; +- col->blue = blue; +- col->alpha = 0xff; +- +- /* XXX: We rotate the nibbles of the colors here so that there is a +- * visible variation between colors of sequential actor identifiers; +- * otherwise pick buffers dumped to an image will pretty much just look +- * black. +- */ +- if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS)) +- { +- col->red = (col->red << 4) | (col->red >> 4); +- col->green = (col->green << 4) | (col->green >> 4); +- col->blue = (col->blue << 4) | (col->blue >> 4); +- } +-} +- +-guint +-_clutter_pixel_to_id (guchar pixel[4]) +-{ +- ClutterMainContext *ctx; +- gint red, green, blue; +- guint retval; +- +- ctx = _clutter_context_get_default (); +- +- /* reduce the pixel components to the number of bits actually used of the +- * 8bits. +- */ +- if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS)) +- { +- guchar tmp; +- +- /* XXX: In _clutter_id_to_color we rotated the nibbles of the colors so +- * that there is a visible variation between colors of sequential actor +- * identifiers (otherwise pick buffers dumped to an image will pretty +- * much just look black.) Here we reverse that rotation. +- */ +- tmp = ((pixel[0] << 4) | (pixel[0] >> 4)); +- red = tmp >> (8 - ctx->fb_r_mask); +- tmp = ((pixel[1] << 4) | (pixel[1] >> 4)); +- green = tmp >> (8 - ctx->fb_g_mask); +- tmp = ((pixel[2] << 4) | (pixel[2] >> 4)); +- blue = tmp >> (8 - ctx->fb_b_mask); +- } +- else +- { +- red = pixel[0] >> (8 - ctx->fb_r_mask); +- green = pixel[1] >> (8 - ctx->fb_g_mask); +- blue = pixel[2] >> (8 - ctx->fb_b_mask); +- } +- +- /* divide potentially by two if 'fuzzy' */ +- red = red >> (ctx->fb_r_mask - ctx->fb_r_mask_used); +- green = green >> (ctx->fb_g_mask - ctx->fb_g_mask_used); +- blue = blue >> (ctx->fb_b_mask - ctx->fb_b_mask_used); +- +- /* combine the correct per component values into the final id */ +- retval = blue +- + (green << ctx->fb_b_mask_used) +- + (red << (ctx->fb_b_mask_used + ctx->fb_g_mask_used)); +- +- return retval; +-} +- + static CoglPangoFontMap * + clutter_context_get_pango_fontmap (void) + { +diff --git a/clutter/clutter/clutter-private.h b/clutter/clutter/clutter-private.h +index 5a0fed85c9..f2f870b014 100644 +--- a/clutter/clutter/clutter-private.h ++++ b/clutter/clutter/clutter-private.h +@@ -210,11 +210,6 @@ gboolean _clutter_feature_init (GError **error); + gboolean _clutter_diagnostic_enabled (void); + void _clutter_diagnostic_message (const char *fmt, ...) G_GNUC_PRINTF (1, 2); + +-/* Picking code */ +-guint _clutter_pixel_to_id (guchar pixel[4]); +-void _clutter_id_to_color (guint id, +- ClutterColor *col); +- + void _clutter_set_sync_to_vblank (gboolean sync_to_vblank); + + /* use this function as the accumulator if you have a signal with +diff --git a/clutter/clutter/clutter-stage-private.h b/clutter/clutter/clutter-stage-private.h +index 42474687ad..51ae47af1d 100644 +--- a/clutter/clutter/clutter-stage-private.h ++++ b/clutter/clutter/clutter-stage-private.h +@@ -75,6 +75,15 @@ gint64 _clutter_stage_get_update_time (ClutterStage *stage); + void _clutter_stage_clear_update_time (ClutterStage *stage); + gboolean _clutter_stage_has_full_redraw_queued (ClutterStage *stage); + ++void clutter_stage_log_pick (ClutterStage *stage, ++ const ClutterPoint *vertices, ++ ClutterActor *actor); ++ ++void clutter_stage_push_pick_clip (ClutterStage *stage, ++ const ClutterPoint *vertices); ++ ++void clutter_stage_pop_pick_clip (ClutterStage *stage); ++ + ClutterActor *_clutter_stage_do_pick (ClutterStage *stage, + gint x, + gint y, +@@ -93,13 +102,6 @@ void _clutter_stage_queue_redraw_entry_invalidate (Clut + + CoglFramebuffer *_clutter_stage_get_active_framebuffer (ClutterStage *stage); + +-gint32 _clutter_stage_acquire_pick_id (ClutterStage *stage, +- ClutterActor *actor); +-void _clutter_stage_release_pick_id (ClutterStage *stage, +- gint32 pick_id); +-ClutterActor * _clutter_stage_get_actor_by_pick_id (ClutterStage *stage, +- gint32 pick_id); +- + void _clutter_stage_add_pointer_drag_actor (ClutterStage *stage, + ClutterInputDevice *device, + ClutterActor *actor); +diff --git a/clutter/clutter/clutter-stage-window.c b/clutter/clutter/clutter-stage-window.c +index e8fa976a7d..4c4ef9d643 100644 +--- a/clutter/clutter/clutter-stage-window.c ++++ b/clutter/clutter/clutter-stage-window.c +@@ -293,24 +293,6 @@ _clutter_stage_window_redraw (ClutterStageWindow *window) + iface->redraw (window); + } + +- +-void +-_clutter_stage_window_get_dirty_pixel (ClutterStageWindow *window, +- ClutterStageView *view, +- int *x, int *y) +-{ +- ClutterStageWindowInterface *iface; +- +- *x = 0; +- *y = 0; +- +- g_return_if_fail (CLUTTER_IS_STAGE_WINDOW (window)); +- +- iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window); +- if (iface->get_dirty_pixel) +- iface->get_dirty_pixel (window, view, x, y); +-} +- + gboolean + _clutter_stage_window_can_clip_redraws (ClutterStageWindow *window) + { +diff --git a/clutter/clutter/clutter-stage-window.h b/clutter/clutter/clutter-stage-window.h +index 6c3601745f..aa0c5f71cc 100644 +--- a/clutter/clutter/clutter-stage-window.h ++++ b/clutter/clutter/clutter-stage-window.h +@@ -68,10 +68,6 @@ struct _ClutterStageWindowInterface + + void (* redraw) (ClutterStageWindow *stage_window); + +- void (* get_dirty_pixel) (ClutterStageWindow *stage_window, +- ClutterStageView *view, +- int *x, int *y); +- + gboolean (* can_clip_redraws) (ClutterStageWindow *stage_window); + + GList *(* get_views) (ClutterStageWindow *stage_window); +@@ -119,10 +115,6 @@ void _clutter_stage_window_set_accept_focus (ClutterStageWin + + void _clutter_stage_window_redraw (ClutterStageWindow *window); + +-void _clutter_stage_window_get_dirty_pixel (ClutterStageWindow *window, +- ClutterStageView *view, +- int *x, int *y); +- + gboolean _clutter_stage_window_can_clip_redraws (ClutterStageWindow *window); + + GList * _clutter_stage_window_get_views (ClutterStageWindow *window); +diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c +index aaa77d9ede..7d88d5752f 100644 +--- a/clutter/clutter/clutter-stage.c ++++ b/clutter/clutter/clutter-stage.c +@@ -97,6 +97,11 @@ typedef enum /*< prefix=CLUTTER_STAGE >*/ + + #define STAGE_NO_CLEAR_ON_PAINT(s) ((((ClutterStage *) (s))->priv->stage_hints & CLUTTER_STAGE_NO_CLEAR_ON_PAINT) != 0) + ++#ifndef G_APPROX_VALUE ++#define G_APPROX_VALUE(a, b, epsilon) \ ++ (((a) > (b) ? (a) - (b) : (b) - (a)) < (epsilon)) ++#endif ++ + struct _ClutterStageQueueRedrawEntry + { + ClutterActor *actor; +@@ -104,6 +109,19 @@ struct _ClutterStageQueueRedrawEntry + ClutterPaintVolume clip; + }; + ++typedef struct _PickRecord ++{ ++ ClutterPoint vertex[4]; ++ ClutterActor *actor; ++ int clip_stack_top; ++} PickRecord; ++ ++typedef struct _PickClipRecord ++{ ++ int prev; ++ ClutterPoint vertex[4]; ++} PickClipRecord; ++ + struct _ClutterStagePrivate + { + /* the stage implementation */ +@@ -137,7 +155,11 @@ struct _ClutterStagePrivate + GTimer *fps_timer; + gint32 timer_n_frames; + +- ClutterIDPool *pick_id_pool; ++ GArray *pick_stack; ++ GArray *pick_clip_stack; ++ int pick_clip_stack_top; ++ gboolean pick_stack_frozen; ++ ClutterPickMode cached_pick_mode; + + #ifdef CLUTTER_ENABLE_DEBUG + gulong redraw_count; +@@ -326,6 +348,211 @@ clutter_stage_get_preferred_height (ClutterActor *self, + *natural_height_p = geom.height; + } + ++static void ++add_pick_stack_weak_refs (ClutterStage *stage) ++{ ++ ClutterStagePrivate *priv = stage->priv; ++ int i; ++ ++ if (priv->pick_stack_frozen) ++ return; ++ ++ for (i = 0; i < priv->pick_stack->len; i++) ++ { ++ PickRecord *rec = &g_array_index (priv->pick_stack, PickRecord, i); ++ ++ if (rec->actor) ++ g_object_add_weak_pointer (G_OBJECT (rec->actor), ++ (gpointer) &rec->actor); ++ } ++ ++ priv->pick_stack_frozen = TRUE; ++} ++ ++static void ++remove_pick_stack_weak_refs (ClutterStage *stage) ++{ ++ ClutterStagePrivate *priv = stage->priv; ++ int i; ++ ++ if (!priv->pick_stack_frozen) ++ return; ++ ++ for (i = 0; i < priv->pick_stack->len; i++) ++ { ++ PickRecord *rec = &g_array_index (priv->pick_stack, PickRecord, i); ++ ++ if (rec->actor) ++ g_object_remove_weak_pointer (G_OBJECT (rec->actor), ++ (gpointer) &rec->actor); ++ } ++ ++ priv->pick_stack_frozen = FALSE; ++} ++ ++static void ++_clutter_stage_clear_pick_stack (ClutterStage *stage) ++{ ++ ClutterStagePrivate *priv = stage->priv; ++ ++ remove_pick_stack_weak_refs (stage); ++ g_array_set_size (priv->pick_stack, 0); ++ g_array_set_size (priv->pick_clip_stack, 0); ++ priv->pick_clip_stack_top = -1; ++ priv->cached_pick_mode = CLUTTER_PICK_NONE; ++} ++ ++void ++clutter_stage_log_pick (ClutterStage *stage, ++ const ClutterPoint *vertices, ++ ClutterActor *actor) ++{ ++ ClutterStagePrivate *priv; ++ PickRecord rec; ++ ++ g_return_if_fail (CLUTTER_IS_STAGE (stage)); ++ g_return_if_fail (actor != NULL); ++ ++ priv = stage->priv; ++ ++ g_assert (!priv->pick_stack_frozen); ++ ++ memcpy (rec.vertex, vertices, 4 * sizeof (ClutterPoint)); ++ rec.actor = actor; ++ rec.clip_stack_top = priv->pick_clip_stack_top; ++ ++ g_array_append_val (priv->pick_stack, rec); ++} ++ ++void ++clutter_stage_push_pick_clip (ClutterStage *stage, ++ const ClutterPoint *vertices) ++{ ++ ClutterStagePrivate *priv; ++ PickClipRecord clip; ++ ++ g_return_if_fail (CLUTTER_IS_STAGE (stage)); ++ ++ priv = stage->priv; ++ ++ g_assert (!priv->pick_stack_frozen); ++ ++ clip.prev = priv->pick_clip_stack_top; ++ memcpy (clip.vertex, vertices, 4 * sizeof (ClutterPoint)); ++ ++ g_array_append_val (priv->pick_clip_stack, clip); ++ priv->pick_clip_stack_top = priv->pick_clip_stack->len - 1; ++} ++ ++void ++clutter_stage_pop_pick_clip (ClutterStage *stage) ++{ ++ ClutterStagePrivate *priv; ++ const PickClipRecord *top; ++ ++ g_return_if_fail (CLUTTER_IS_STAGE (stage)); ++ ++ priv = stage->priv; ++ ++ g_assert (!priv->pick_stack_frozen); ++ g_assert (priv->pick_clip_stack_top >= 0); ++ ++ /* Individual elements of pick_clip_stack are not freed. This is so they ++ * can be shared as part of a tree of different stacks used by different ++ * actors in the pick_stack. The whole pick_clip_stack does however get ++ * freed later in _clutter_stage_clear_pick_stack. ++ */ ++ ++ top = &g_array_index (priv->pick_clip_stack, ++ PickClipRecord, ++ priv->pick_clip_stack_top); ++ ++ priv->pick_clip_stack_top = top->prev; ++} ++ ++static gboolean ++is_quadrilateral_axis_aligned_rectangle (const ClutterPoint *vertices) ++{ ++ int i; ++ ++ for (i = 0; i < 4; i++) ++ { ++ if (!G_APPROX_VALUE (vertices[i].x, ++ vertices[(i + 1) % 4].x, ++ FLT_EPSILON) && ++ !G_APPROX_VALUE (vertices[i].y, ++ vertices[(i + 1) % 4].y, ++ FLT_EPSILON)) ++ return FALSE; ++ } ++ return TRUE; ++} ++ ++static gboolean ++is_inside_axis_aligned_rectangle (const ClutterPoint *point, ++ const ClutterPoint *vertices) ++{ ++ float min_x = FLT_MAX; ++ float max_x = FLT_MIN; ++ float min_y = FLT_MAX; ++ float max_y = FLT_MIN; ++ int i; ++ ++ for (i = 0; i < 3; i++) ++ { ++ min_x = MIN (min_x, vertices[i].x); ++ min_y = MIN (min_y, vertices[i].y); ++ max_x = MAX (max_x, vertices[i].x); ++ max_y = MAX (max_y, vertices[i].y); ++ } ++ ++ return (point->x >= min_x && ++ point->y >= min_y && ++ point->x < max_x && ++ point->y < max_y); ++} ++ ++static gboolean ++is_inside_input_region (const ClutterPoint *point, ++ const ClutterPoint *vertices) ++{ ++ ++ if (is_quadrilateral_axis_aligned_rectangle (vertices)) ++ return is_inside_axis_aligned_rectangle (point, vertices); ++ else ++ return clutter_point_inside_quadrilateral (point, vertices); ++} ++ ++static gboolean ++pick_record_contains_pixel (ClutterStage *stage, ++ const PickRecord *rec, ++ int x, ++ int y) ++{ ++ const ClutterPoint point = CLUTTER_POINT_INIT (x, y); ++ ClutterStagePrivate *priv; ++ int clip_index; ++ ++ if (!is_inside_input_region (&point, rec->vertex)) ++ return FALSE; ++ ++ priv = stage->priv; ++ clip_index = rec->clip_stack_top; ++ while (clip_index >= 0) ++ { ++ const PickClipRecord *clip = &g_array_index (priv->pick_clip_stack, ++ PickClipRecord, ++ clip_index); ++ ++ if (!is_inside_input_region (&point, clip->vertex)) ++ return FALSE; ++ ++ clip_index = clip->prev; ++ } ++ ++ return TRUE; ++} ++ + static inline void + queue_full_redraw (ClutterStage *stage) + { +@@ -636,6 +863,12 @@ clutter_stage_do_paint_view (ClutterStage *stage, + float viewport[4]; + cairo_rectangle_int_t geom; + ++ /* Any mode of painting/picking invalidates the pick cache, unless we're ++ * in the middle of building it. So we reset the cached flag but don't ++ * completely clear the pick stack. ++ */ ++ priv->cached_pick_mode = CLUTTER_PICK_NONE; ++ + _clutter_stage_window_get_geometry (priv->impl, &geom); + + viewport[0] = priv->viewport[0]; +@@ -1414,40 +1647,6 @@ clutter_stage_get_redraw_clip_bounds (ClutterStage *stage, + } + } + +-static void +-read_pixels_to_file (CoglFramebuffer *fb, +- char *filename_stem, +- int x, +- int y, +- int width, +- int height) +-{ +- guint8 *data; +- cairo_surface_t *surface; +- static int read_count = 0; +- char *filename = g_strdup_printf ("%s-%05d.png", +- filename_stem, +- read_count); +- +- data = g_malloc (4 * width * height); +- cogl_framebuffer_read_pixels (fb, +- x, y, width, height, +- CLUTTER_CAIRO_FORMAT_ARGB32, +- data); +- +- surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_RGB24, +- width, height, +- width * 4); +- +- cairo_surface_write_to_png (surface, filename); +- cairo_surface_destroy (surface); +- +- g_free (data); +- g_free (filename); +- +- read_count++; +-} +- + static ClutterActor * + _clutter_stage_do_pick_on_view (ClutterStage *stage, + gint x, +@@ -1455,140 +1654,42 @@ _clutter_stage_do_pick_on_view (ClutterStage *stage, + ClutterPickMode mode, + ClutterStageView *view) + { +- ClutterActor *actor = CLUTTER_ACTOR (stage); ++ ClutterMainContext *context = _clutter_context_get_default (); + ClutterStagePrivate *priv = stage->priv; + CoglFramebuffer *fb = clutter_stage_view_get_framebuffer (view); +- cairo_rectangle_int_t view_layout; +- ClutterMainContext *context; +- guchar pixel[4] = { 0xff, 0xff, 0xff, 0xff }; +- CoglColor stage_pick_id; +- gboolean dither_enabled_save; +- ClutterActor *retval; +- gint dirty_x; +- gint dirty_y; +- gint read_x; +- gint read_y; +- float fb_width, fb_height; +- float fb_scale; +- float viewport_offset_x; +- float viewport_offset_y; +- +- priv = stage->priv; +- +- context = _clutter_context_get_default (); +- fb_scale = clutter_stage_view_get_scale (view); +- clutter_stage_view_get_layout (view, &view_layout); +- +- fb_width = view_layout.width * fb_scale; +- fb_height = view_layout.height * fb_scale; +- cogl_push_framebuffer (fb); +- +- /* needed for when a context switch happens */ +- _clutter_stage_maybe_setup_viewport (stage, view); +- +- /* FIXME: For some reason leaving the cogl clip stack empty causes the +- * picking to not work at all, so setting it the whole framebuffer content +- * for now. */ +- cogl_framebuffer_push_scissor_clip (fb, 0, 0, +- view_layout.width * fb_scale, +- view_layout.height * fb_scale); +- +- _clutter_stage_window_get_dirty_pixel (priv->impl, view, &dirty_x, &dirty_y); ++ int i; + +- if (G_LIKELY (!(clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))) +- { +- CLUTTER_NOTE (PICK, "Pushing pick scissor clip x: %d, y: %d, 1x1", +- (int) (dirty_x * fb_scale), +- (int) (dirty_y * fb_scale)); +- cogl_framebuffer_push_scissor_clip (fb, dirty_x * fb_scale, dirty_y * fb_scale, 1, 1); +- } ++ g_assert (context->pick_mode == CLUTTER_PICK_NONE); + +- viewport_offset_x = x * fb_scale - dirty_x * fb_scale; +- viewport_offset_y = y * fb_scale - dirty_y * fb_scale; +- CLUTTER_NOTE (PICK, "Setting viewport to %f, %f, %f, %f", +- priv->viewport[0] * fb_scale - viewport_offset_x, +- priv->viewport[1] * fb_scale - viewport_offset_y, +- priv->viewport[2] * fb_scale, +- priv->viewport[3] * fb_scale); +- cogl_framebuffer_set_viewport (fb, +- priv->viewport[0] * fb_scale - viewport_offset_x, +- priv->viewport[1] * fb_scale - viewport_offset_y, +- priv->viewport[2] * fb_scale, +- priv->viewport[3] * fb_scale); +- +- read_x = dirty_x * fb_scale; +- read_y = dirty_y * fb_scale; +- +- CLUTTER_NOTE (PICK, "Performing pick at %i,%i on view %dx%d+%d+%d s: %f", +- x, y, +- view_layout.width, view_layout.height, +- view_layout.x, view_layout.y, fb_scale); +- +- cogl_color_init_from_4ub (&stage_pick_id, 255, 255, 255, 255); +- cogl_framebuffer_clear (fb, COGL_BUFFER_BIT_COLOR | COGL_BUFFER_BIT_DEPTH, &stage_pick_id); +- +- /* Disable dithering (if any) when doing the painting in pick mode */ +- dither_enabled_save = cogl_framebuffer_get_dither_enabled (fb); +- cogl_framebuffer_set_dither_enabled (fb, FALSE); +- +- /* Render the entire scence in pick mode - just single colored silhouette's +- * are drawn offscreen (as we never swap buffers) +- */ +- context->pick_mode = mode; +- +- clutter_stage_do_paint_view (stage, view, NULL); +- context->pick_mode = CLUTTER_PICK_NONE; +- +- /* Read the color of the screen co-ords pixel. RGBA_8888_PRE is used +- even though we don't care about the alpha component because under +- GLES this is the only format that is guaranteed to work so Cogl +- will end up having to do a conversion if any other format is +- used. The format is requested as pre-multiplied because Cogl +- assumes that all pixels in the framebuffer are premultiplied so +- it avoids a conversion. */ +- cogl_framebuffer_read_pixels (fb, +- read_x, read_y, 1, 1, +- COGL_PIXEL_FORMAT_RGBA_8888_PRE, +- pixel); +- +- if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS)) ++ if (mode != priv->cached_pick_mode) + { +- char *file_name = +- g_strdup_printf ("pick-buffer-%s-view-x-%d", +- _clutter_actor_get_debug_name (actor), +- view_layout.x); ++ _clutter_stage_clear_pick_stack (stage); + +- read_pixels_to_file (fb, file_name, 0, 0, fb_width, fb_height); ++ cogl_push_framebuffer (fb); + +- g_free (file_name); +- } +- +- /* Restore whether GL_DITHER was enabled */ +- cogl_framebuffer_set_dither_enabled (fb, dither_enabled_save); +- +- if (G_LIKELY (!(clutter_pick_debug_flags & CLUTTER_DEBUG_DUMP_PICK_BUFFERS))) +- cogl_framebuffer_pop_clip (fb); ++ context->pick_mode = mode; ++ clutter_stage_do_paint_view (stage, view, NULL); ++ context->pick_mode = CLUTTER_PICK_NONE; ++ priv->cached_pick_mode = mode; + +- cogl_framebuffer_pop_clip (fb); ++ cogl_pop_framebuffer (); + +- _clutter_stage_dirty_viewport (stage); ++ add_pick_stack_weak_refs (stage); ++ } + +- if (pixel[0] == 0xff && pixel[1] == 0xff && pixel[2] == 0xff) +- retval = actor; +- else ++ /* Search all "painted" pickable actors from front to back. A linear search ++ * is required, and also performs fine since there is typically only ++ * on the order of dozens of actors in the list (on screen) at a time. ++ */ ++ for (i = priv->pick_stack->len - 1; i >= 0; i--) + { +- guint32 id_ = _clutter_pixel_to_id (pixel); ++ const PickRecord *rec = &g_array_index (priv->pick_stack, PickRecord, i); + +- retval = _clutter_stage_get_actor_by_pick_id (stage, id_); +- CLUTTER_NOTE (PICK, "Picking actor %s with id %u (pixel: 0x%x%x%x%x", +- G_OBJECT_TYPE_NAME (retval), +- id_, +- pixel[0], pixel[1], pixel[2], pixel[3]); ++ if (rec->actor && pick_record_contains_pixel (stage, rec, x, y)) ++ return rec->actor; + } + +- cogl_pop_framebuffer (); +- +- return retval; ++ return CLUTTER_ACTOR (stage); + } + + static ClutterStageView * +@@ -1901,7 +2002,9 @@ clutter_stage_finalize (GObject *object) + + g_array_free (priv->paint_volume_stack, TRUE); + +- _clutter_id_pool_free (priv->pick_id_pool); ++ _clutter_stage_clear_pick_stack (stage); ++ g_array_free (priv->pick_clip_stack, TRUE); ++ g_array_free (priv->pick_stack, TRUE); + + if (priv->fps_timer != NULL) + g_timer_destroy (priv->fps_timer); +@@ -2435,7 +2538,10 @@ clutter_stage_init (ClutterStage *self) + priv->paint_volume_stack = + g_array_new (FALSE, FALSE, sizeof (ClutterPaintVolume)); + +- priv->pick_id_pool = _clutter_id_pool_new (256); ++ priv->pick_stack = g_array_new (FALSE, FALSE, sizeof (PickRecord)); ++ priv->pick_clip_stack = g_array_new (FALSE, FALSE, sizeof (PickClipRecord)); ++ priv->pick_clip_stack_top = -1; ++ priv->cached_pick_mode = CLUTTER_PICK_NONE; + } + + /** +@@ -4253,6 +4359,12 @@ _clutter_stage_queue_actor_redraw (ClutterStage *stage, + CLUTTER_NOTE (CLIPPING, "stage_queue_actor_redraw (actor=%s, clip=%p): ", + _clutter_actor_get_debug_name (actor), clip); + ++ /* Queuing a redraw or clip change invalidates the pick cache, unless we're ++ * in the middle of building it. So we reset the cached flag but don't ++ * completely clear the pick stack... ++ */ ++ priv->cached_pick_mode = CLUTTER_PICK_NONE; ++ + if (!priv->redraw_pending) + { + ClutterMasterClock *master_clock; +@@ -4513,39 +4625,6 @@ _clutter_stage_get_active_framebuffer (ClutterStage *stage) + return stage->priv->active_framebuffer; + } + +-gint32 +-_clutter_stage_acquire_pick_id (ClutterStage *stage, +- ClutterActor *actor) +-{ +- ClutterStagePrivate *priv = stage->priv; +- +- g_assert (priv->pick_id_pool != NULL); +- +- return _clutter_id_pool_add (priv->pick_id_pool, actor); +-} +- +-void +-_clutter_stage_release_pick_id (ClutterStage *stage, +- gint32 pick_id) +-{ +- ClutterStagePrivate *priv = stage->priv; +- +- g_assert (priv->pick_id_pool != NULL); +- +- _clutter_id_pool_remove (priv->pick_id_pool, pick_id); +-} +- +-ClutterActor * +-_clutter_stage_get_actor_by_pick_id (ClutterStage *stage, +- gint32 pick_id) +-{ +- ClutterStagePrivate *priv = stage->priv; +- +- g_assert (priv->pick_id_pool != NULL); +- +- return _clutter_id_pool_lookup (priv->pick_id_pool, pick_id); +-} +- + void + _clutter_stage_add_pointer_drag_actor (ClutterStage *stage, + ClutterInputDevice *device, +diff --git a/clutter/clutter/cogl/clutter-stage-cogl.c b/clutter/clutter/cogl/clutter-stage-cogl.c +index 3f1f609c4e..effed79759 100644 +--- a/clutter/clutter/cogl/clutter-stage-cogl.c ++++ b/clutter/clutter/cogl/clutter-stage-cogl.c +@@ -926,57 +926,6 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window) + stage_cogl->frame_count++; + } + +-static void +-clutter_stage_cogl_get_dirty_pixel (ClutterStageWindow *stage_window, +- ClutterStageView *view, +- int *x, +- int *y) +-{ +- CoglFramebuffer *onscreen = clutter_stage_view_get_onscreen (view); +- ClutterStageViewCogl *view_cogl = CLUTTER_STAGE_VIEW_COGL (view); +- ClutterStageViewCoglPrivate *view_priv = +- clutter_stage_view_cogl_get_instance_private (view_cogl); +- gboolean has_buffer_age = +- cogl_is_onscreen (onscreen) && +- is_buffer_age_enabled (); +- float fb_scale; +- gboolean scale_is_fractional; +- +- fb_scale = clutter_stage_view_get_scale (view); +- if (fb_scale != floorf (fb_scale)) +- scale_is_fractional = TRUE; +- else +- scale_is_fractional = FALSE; +- +- /* +- * Buffer damage is tracked in the framebuffer coordinate space +- * using the damage history. When fractional scaling is used, a +- * coordinate on the stage might not correspond to the exact position of any +- * physical pixel, which causes issues when painting using the pick mode. +- * +- * For now, always use the (0, 0) pixel for picking when using fractional +- * framebuffer scaling. +- */ +- if (!has_buffer_age || +- scale_is_fractional || +- !clutter_damage_history_is_age_valid (view_priv->damage_history, 0)) +- { +- *x = 0; +- *y = 0; +- } +- else +- { +- cairo_rectangle_int_t view_layout; +- const cairo_rectangle_int_t *fb_damage; +- +- clutter_stage_view_get_layout (view, &view_layout); +- +- fb_damage = clutter_damage_history_lookup (view_priv->damage_history, 0); +- *x = fb_damage->x / fb_scale; +- *y = fb_damage->y / fb_scale; +- } +-} +- + static void + clutter_stage_window_iface_init (ClutterStageWindowInterface *iface) + { +@@ -994,7 +943,6 @@ clutter_stage_window_iface_init (ClutterStageWindowInterface *iface) + iface->ignoring_redraw_clips = clutter_stage_cogl_ignoring_redraw_clips; + iface->get_redraw_clip_bounds = clutter_stage_cogl_get_redraw_clip_bounds; + iface->redraw = clutter_stage_cogl_redraw; +- iface->get_dirty_pixel = clutter_stage_cogl_get_dirty_pixel; + } + + static void +diff --git a/clutter/clutter/deprecated/clutter-texture.c b/clutter/clutter/deprecated/clutter-texture.c +index bea239f454..2c677b8a44 100644 +--- a/clutter/clutter/deprecated/clutter-texture.c ++++ b/clutter/clutter/deprecated/clutter-texture.c +@@ -572,83 +572,6 @@ gen_texcoords_and_draw_cogl_rectangle (ClutterActor *self, + 0, 0, t_w, t_h); + } + +-static CoglPipeline * +-create_pick_pipeline (ClutterActor *self) +-{ +- ClutterTexture *texture = CLUTTER_TEXTURE (self); +- ClutterTexturePrivate *priv = texture->priv; +- CoglPipeline *pick_pipeline = cogl_pipeline_copy (texture_template_pipeline); +- GError *error = NULL; +- +- if (!cogl_pipeline_set_layer_combine (pick_pipeline, 0, +- "RGBA = " +- " MODULATE (CONSTANT, TEXTURE[A])", +- &error)) +- { +- if (!priv->seen_create_pick_pipeline_warning) +- g_warning ("Error setting up texture combine for shaped " +- "texture picking: %s", error->message); +- priv->seen_create_pick_pipeline_warning = TRUE; +- g_error_free (error); +- cogl_object_unref (pick_pipeline); +- return NULL; +- } +- +- cogl_pipeline_set_blend (pick_pipeline, +- "RGBA = ADD (SRC_COLOR[RGBA], 0)", +- NULL); +- +- cogl_pipeline_set_alpha_test_function (pick_pipeline, +- COGL_PIPELINE_ALPHA_FUNC_EQUAL, +- 1.0); +- +- return pick_pipeline; +-} +- +-static void +-clutter_texture_pick (ClutterActor *self, +- const ClutterColor *color) +-{ +- ClutterTexture *texture = CLUTTER_TEXTURE (self); +- ClutterTexturePrivate *priv = texture->priv; +- CoglFramebuffer *framebuffer = cogl_get_draw_framebuffer (); +- +- if (!clutter_actor_should_pick_paint (self)) +- return; +- +- if (G_LIKELY (priv->pick_with_alpha_supported) && priv->pick_with_alpha) +- { +- CoglColor pick_color; +- +- if (priv->pick_pipeline == NULL) +- priv->pick_pipeline = create_pick_pipeline (self); +- +- if (priv->pick_pipeline == NULL) +- { +- priv->pick_with_alpha_supported = FALSE; +- CLUTTER_ACTOR_CLASS (clutter_texture_parent_class)->pick (self, +- color); +- return; +- } +- +- if (priv->fbo_handle != NULL) +- update_fbo (self); +- +- cogl_color_init_from_4ub (&pick_color, +- color->red, +- color->green, +- color->blue, +- 0xff); +- cogl_pipeline_set_layer_combine_constant (priv->pick_pipeline, +- 0, &pick_color); +- cogl_pipeline_set_layer_texture (priv->pick_pipeline, 0, +- clutter_texture_get_cogl_texture (texture)); +- gen_texcoords_and_draw_cogl_rectangle (self, priv->pick_pipeline, framebuffer); +- } +- else +- CLUTTER_ACTOR_CLASS (clutter_texture_parent_class)->pick (self, color); +-} +- + static void + clutter_texture_paint (ClutterActor *self) + { +@@ -767,12 +690,6 @@ clutter_texture_dispose (GObject *object) + priv->pipeline = NULL; + } + +- if (priv->pick_pipeline != NULL) +- { +- cogl_object_unref (priv->pick_pipeline); +- priv->pick_pipeline = NULL; +- } +- + G_OBJECT_CLASS (clutter_texture_parent_class)->dispose (object); + } + +@@ -944,7 +861,6 @@ clutter_texture_class_init (ClutterTextureClass *klass) + GParamSpec *pspec; + + actor_class->paint = clutter_texture_paint; +- actor_class->pick = clutter_texture_pick; + actor_class->get_paint_volume = clutter_texture_get_paint_volume; + actor_class->realize = clutter_texture_realize; + actor_class->unrealize = clutter_texture_unrealize; +@@ -1263,11 +1179,9 @@ clutter_texture_init (ClutterTexture *self) + priv->repeat_y = FALSE; + priv->sync_actor_size = TRUE; + priv->fbo_handle = NULL; +- priv->pick_pipeline = NULL; + priv->keep_aspect_ratio = FALSE; + priv->pick_with_alpha = FALSE; + priv->pick_with_alpha_supported = TRUE; +- priv->seen_create_pick_pipeline_warning = FALSE; + + if (G_UNLIKELY (texture_template_pipeline == NULL)) + { +@@ -3052,13 +2966,8 @@ clutter_texture_set_pick_with_alpha (ClutterTexture *texture, + if (priv->pick_with_alpha == pick_with_alpha) + return; + +- if (!pick_with_alpha && priv->pick_pipeline != NULL) +- { +- cogl_object_unref (priv->pick_pipeline); +- priv->pick_pipeline = NULL; +- } ++ g_assert (!pick_with_alpha); /* No longer supported */ + +- /* NB: the pick pipeline is created lazily when we first pick */ + priv->pick_with_alpha = pick_with_alpha; + + /* NB: actors are expected to call clutter_actor_queue_redraw when +diff --git a/clutter/tests/conform/actor-pick.c b/clutter/tests/conform/actor-pick.c +index 969b4920ac..2bf5954c73 100644 +--- a/clutter/tests/conform/actor-pick.c ++++ b/clutter/tests/conform/actor-pick.c +@@ -5,7 +5,6 @@ + #define STAGE_HEIGHT 480 + #define ACTORS_X 12 + #define ACTORS_Y 16 +-#define SHIFT_STEP STAGE_WIDTH / ACTORS_X + + typedef struct _State State; + +@@ -20,84 +19,11 @@ struct _State + gboolean pass; + }; + +-struct _ShiftEffect +-{ +- ClutterShaderEffect parent_instance; +-}; +- +-struct _ShiftEffectClass +-{ +- ClutterShaderEffectClass parent_class; +-}; +- +-typedef struct _ShiftEffect ShiftEffect; +-typedef struct _ShiftEffectClass ShiftEffectClass; +- +-#define TYPE_SHIFT_EFFECT (shift_effect_get_type ()) +- +-GType shift_effect_get_type (void); +- +-G_DEFINE_TYPE (ShiftEffect, +- shift_effect, +- CLUTTER_TYPE_SHADER_EFFECT); +- +-static void +-shader_paint (ClutterEffect *effect, +- ClutterEffectPaintFlags flags) +-{ +- ClutterShaderEffect *shader = CLUTTER_SHADER_EFFECT (effect); +- float tex_width; +- ClutterActor *actor = +- clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect)); +- +- if (g_test_verbose ()) +- g_debug ("shader_paint"); +- +- clutter_shader_effect_set_shader_source (shader, +- "uniform sampler2D tex;\n" +- "uniform float step;\n" +- "void main (void)\n" +- "{\n" +- " cogl_color_out = texture2D(tex, vec2 (cogl_tex_coord_in[0].s + step,\n" +- " cogl_tex_coord_in[0].t));\n" +- "}\n"); +- +- tex_width = clutter_actor_get_width (actor); +- +- clutter_shader_effect_set_uniform (shader, "tex", G_TYPE_INT, 1, 0); +- clutter_shader_effect_set_uniform (shader, "step", G_TYPE_FLOAT, 1, +- SHIFT_STEP / tex_width); +- +- CLUTTER_EFFECT_CLASS (shift_effect_parent_class)->paint (effect, flags); +-} +- +-static void +-shader_pick (ClutterEffect *effect, +- ClutterEffectPaintFlags flags) +-{ +- shader_paint (effect, flags); +-} +- +-static void +-shift_effect_class_init (ShiftEffectClass *klass) +-{ +- ClutterEffectClass *shader_class = CLUTTER_EFFECT_CLASS (klass); +- +- shader_class->paint = shader_paint; +- shader_class->pick = shader_pick; +-} +- +-static void +-shift_effect_init (ShiftEffect *self) +-{ +-} +- + static const char *test_passes[] = { + "No covering actor", + "Invisible covering actor", + "Clipped covering actor", + "Blur effect", +- "Shift effect", + }; + + static gboolean +@@ -165,30 +91,10 @@ on_timeout (gpointer data) + if (g_test_verbose ()) + g_print ("With blur effect:\n"); + } +- else if (test_num == 4) +- { +- if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL)) +- continue; +- +- clutter_actor_hide (over_actor); +- clutter_actor_remove_effect_by_name (CLUTTER_ACTOR (state->stage), +- "blur"); +- +- clutter_actor_add_effect_with_name (CLUTTER_ACTOR (state->stage), +- "shift", +- g_object_new (TYPE_SHIFT_EFFECT, +- NULL)); +- +- if (g_test_verbose ()) +- g_print ("With shift effect:\n"); +- } + + for (y = 0; y < ACTORS_Y; y++) + { +- if (test_num == 4) +- x = 1; +- else +- x = 0; ++ x = 0; + + for (; x < ACTORS_X; x++) + { +@@ -198,9 +104,6 @@ on_timeout (gpointer data) + + pick_x = x * state->actor_width + state->actor_width / 2; + +- if (test_num == 4) +- pick_x -= SHIFT_STEP; +- + actor = + clutter_stage_get_actor_at_pos (CLUTTER_STAGE (state->stage), + CLUTTER_PICK_ALL, +diff --git a/clutter/tests/conform/meson.build b/clutter/tests/conform/meson.build +index a9f2d7e20c..fffc9014c4 100644 +--- a/clutter/tests/conform/meson.build ++++ b/clutter/tests/conform/meson.build +@@ -42,7 +42,6 @@ clutter_conform_tests_deprecated_tests = [ + 'behaviours', + 'group', + 'rectangle', +- 'texture', + ] + + clutter_conform_tests = [] +diff --git a/clutter/tests/conform/texture.c b/clutter/tests/conform/texture.c +deleted file mode 100644 +index 392fd5c47e..0000000000 +--- a/clutter/tests/conform/texture.c ++++ /dev/null +@@ -1,84 +0,0 @@ +-#define CLUTTER_DISABLE_DEPRECATION_WARNINGS +-#include +-#include +- +-static CoglHandle +-make_texture (void) +-{ +- guint32 *data = g_malloc (100 * 100 * 4); +- int x; +- int y; +- +- for (y = 0; y < 100; y ++) +- for (x = 0; x < 100; x++) +- { +- if (x < 50 && y < 50) +- data[y * 100 + x] = 0xff00ff00; +- else +- data[y * 100 + x] = 0xff00ffff; +- } +- return cogl_texture_new_from_data (100, +- 100, +- COGL_TEXTURE_NONE, +- COGL_PIXEL_FORMAT_ARGB_8888, +- COGL_PIXEL_FORMAT_ARGB_8888, +- 400, +- (guchar *)data); +-} +- +-static void +-texture_pick_with_alpha (void) +-{ +- ClutterTexture *tex = CLUTTER_TEXTURE (clutter_texture_new ()); +- ClutterStage *stage = CLUTTER_STAGE (clutter_test_get_stage ()); +- ClutterActor *actor; +- +- clutter_texture_set_cogl_texture (tex, make_texture ()); +- +- clutter_actor_add_child (CLUTTER_ACTOR (stage), CLUTTER_ACTOR (tex)); +- +- clutter_actor_show (CLUTTER_ACTOR (stage)); +- +- if (g_test_verbose ()) +- { +- g_print ("\nstage = %p\n", stage); +- g_print ("texture = %p\n\n", tex); +- } +- +- clutter_texture_set_pick_with_alpha (tex, TRUE); +- if (g_test_verbose ()) +- g_print ("Testing with pick-with-alpha enabled:\n"); +- +- /* This should fall through and hit the stage: */ +- actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_ALL, 10, 10); +- if (g_test_verbose ()) +- g_print ("actor @ (10, 10) = %p\n", actor); +- g_assert (actor == CLUTTER_ACTOR (stage)); +- +- /* The rest should hit the texture */ +- actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_ALL, 90, 10); +- if (g_test_verbose ()) +- g_print ("actor @ (90, 10) = %p\n", actor); +- g_assert (actor == CLUTTER_ACTOR (tex)); +- actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_ALL, 90, 90); +- if (g_test_verbose ()) +- g_print ("actor @ (90, 90) = %p\n", actor); +- g_assert (actor == CLUTTER_ACTOR (tex)); +- actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_ALL, 10, 90); +- if (g_test_verbose ()) +- g_print ("actor @ (10, 90) = %p\n", actor); +- g_assert (actor == CLUTTER_ACTOR (tex)); +- +- clutter_texture_set_pick_with_alpha (tex, FALSE); +- if (g_test_verbose ()) +- g_print ("Testing with pick-with-alpha disabled:\n"); +- +- actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_ALL, 10, 10); +- if (g_test_verbose ()) +- g_print ("actor @ (10, 10) = %p\n", actor); +- g_assert (actor == CLUTTER_ACTOR (tex)); +-} +- +-CLUTTER_TEST_SUITE ( +- CLUTTER_TEST_UNIT ("/texture/pick-with-alpha", texture_pick_with_alpha) +-) +diff --git a/src/compositor/meta-surface-actor.c b/src/compositor/meta-surface-actor.c +index ca4ca19a99..814199145a 100644 +--- a/src/compositor/meta-surface-actor.c ++++ b/src/compositor/meta-surface-actor.c +@@ -70,38 +70,23 @@ meta_surface_actor_pick (ClutterActor *actor, + else + { + int n_rects; +- float *rectangles; + int i; +- CoglPipeline *pipeline; +- CoglContext *ctx; +- CoglFramebuffer *fb; +- CoglColor cogl_color; + + n_rects = cairo_region_num_rectangles (priv->input_region); +- rectangles = g_alloca (sizeof (float) * 4 * n_rects); + + for (i = 0; i < n_rects; i++) + { + cairo_rectangle_int_t rect; +- int pos = i * 4; ++ ClutterActorBox box; + + cairo_region_get_rectangle (priv->input_region, i, &rect); + +- rectangles[pos + 0] = rect.x; +- rectangles[pos + 1] = rect.y; +- rectangles[pos + 2] = rect.x + rect.width; +- rectangles[pos + 3] = rect.y + rect.height; ++ box.x1 = rect.x; ++ box.y1 = rect.y; ++ box.x2 = rect.x + rect.width; ++ box.y2 = rect.y + rect.height; ++ clutter_actor_pick_box (actor, &box); + } +- +- ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); +- fb = cogl_get_draw_framebuffer (); +- +- cogl_color_init_from_4ub (&cogl_color, color->red, color->green, color->blue, color->alpha); +- +- pipeline = cogl_pipeline_new (ctx); +- cogl_pipeline_set_color (pipeline, &cogl_color); +- cogl_framebuffer_draw_rectangles (fb, pipeline, rectangles, n_rects); +- cogl_object_unref (pipeline); + } + + clutter_actor_iter_init (&iter, actor); +-- +2.29.2 + + +From 254e93de8d60393ca94fa430c0acc6f6a7b9516e Mon Sep 17 00:00:00 2001 +From: Iain Lane +Date: Mon, 9 Sep 2019 10:17:22 +0100 +Subject: [PATCH 3/4] build: Compile with `-ffloat-store` on x86 (32 bit) + +GCC's manpage says that this flag does the following: + + Do not store floating-point variables in registers, and inhibit other + options that might change whether a floating-point value is taken from + a register or memory. + + This option prevents undesirable excess precision on machines such as + the 68000 where the floating registers (of the 68881) keep more + precision than a "double" is supposed to have. Similarly for the x86 + architecture. For most programs, the excess precision does only good, + but a few programs rely on the precise definition of IEEE floating + point. + +We rely on this behaviour in our fork of clutter. When performing +floating point computations on x86, we are getting the wrong results +because of this architecture's use of the CPU's extended (x87, non-IEEE +confirming) precision by default. If we enable `-ffloat-store` here, +then we'll get the same results everywhere by storing into variables +instead of registers. This does not remove the need to be correct when +handling floats, but it does mean we don't need to be more correct than +the IEEE spec requires. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/785 +--- + meson.build | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/meson.build b/meson.build +index 8ef592bc58..e1edb78ba7 100644 +--- a/meson.build ++++ b/meson.build +@@ -267,6 +267,9 @@ foreach function : required_functions + endif + endforeach + ++if host_machine.cpu_family() == 'x86' ++ add_project_arguments('-ffloat-store', language: 'c') ++endif + add_project_arguments('-D_GNU_SOURCE', language: 'c') + + all_warnings = [ +-- +2.29.2 + + +From 2d42caef14772984344e62ce40957d3b40e1f2b6 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= +Date: Thu, 19 Sep 2019 11:27:50 +0200 +Subject: [PATCH 4/4] stage: Compute view perspective when parameters changed + +Clutter stage used to compute the initial projection using a fixed z +translation which wasn't matching the one we computed in +calculate_z_translation(). +This caused to have a wrong initial projection on startup which was then +correctly recomputed only at the first paint. + +However, since this calculation doesn't depend on view, but only on viewport +size, perspective's fovy and z_near we can safely do this at startup and +only when any of those parameters change. + +Then we can move the computation out _clutter_stage_maybe_setup_viewport() +since the cogl framebuffer viewport sizes aren't affecting this. + +Fixes https://gitlab.gnome.org/GNOME/gnome-shell/issues/1639 +https://gitlab.gnome.org/GNOME/mutter/merge_requests/803 +--- + clutter/clutter/clutter-stage.c | 104 +++++++++++++++----------------- + 1 file changed, 47 insertions(+), 57 deletions(-) + +diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c +index 7d88d5752f..0cfa87486e 100644 +--- a/clutter/clutter/clutter-stage.c ++++ b/clutter/clutter/clutter-stage.c +@@ -234,6 +234,7 @@ static void capture_view_into (ClutterStage *stage, + cairo_rectangle_int_t *rect, + uint8_t *data, + int stride); ++static void clutter_stage_update_view_perspective (ClutterStage *stage); + + static void clutter_container_iface_init (ClutterContainerIface *iface); + +@@ -2492,29 +2493,6 @@ clutter_stage_init (ClutterStage *self) + clutter_actor_set_background_color (CLUTTER_ACTOR (self), + &default_stage_color); + +- priv->perspective.fovy = 60.0; /* 60 Degrees */ +- priv->perspective.aspect = (float) geom.width / (float) geom.height; +- priv->perspective.z_near = 0.1; +- priv->perspective.z_far = 100.0; +- +- cogl_matrix_init_identity (&priv->projection); +- cogl_matrix_perspective (&priv->projection, +- priv->perspective.fovy, +- priv->perspective.aspect, +- priv->perspective.z_near, +- priv->perspective.z_far); +- cogl_matrix_get_inverse (&priv->projection, +- &priv->inverse_projection); +- cogl_matrix_init_identity (&priv->view); +- cogl_matrix_view_2d_in_perspective (&priv->view, +- priv->perspective.fovy, +- priv->perspective.aspect, +- priv->perspective.z_near, +- 50, /* distance to 2d plane */ +- geom.width, +- geom.height); +- +- + /* FIXME - remove for 2.0 */ + priv->fog.z_near = 1.0; + priv->fog.z_far = 2.0; +@@ -2682,6 +2660,7 @@ clutter_stage_set_perspective (ClutterStage *stage, + priv->has_custom_perspective = TRUE; + + clutter_stage_set_perspective_internal (stage, perspective); ++ clutter_stage_update_view_perspective (stage); + } + + /** +@@ -2808,6 +2787,7 @@ _clutter_stage_set_viewport (ClutterStage *stage, + priv->viewport[2] = width; + priv->viewport[3] = height; + ++ clutter_stage_update_view_perspective (stage); + _clutter_stage_dirty_viewport (stage); + + queue_full_redraw (stage); +@@ -3788,6 +3768,50 @@ calculate_z_translation (float z_near) + + z_near; + } + ++static void ++clutter_stage_update_view_perspective (ClutterStage *stage) ++{ ++ ClutterStagePrivate *priv = stage->priv; ++ ClutterPerspective perspective; ++ float z_2d; ++ ++ perspective = priv->perspective; ++ ++ /* Ideally we want to regenerate the perspective matrix whenever ++ * the size changes but if the user has provided a custom matrix ++ * then we don't want to override it */ ++ if (!priv->has_custom_perspective) ++ { ++ perspective.fovy = 60.0; /* 60 Degrees */ ++ perspective.z_near = 0.1; ++ perspective.aspect = priv->viewport[2] / priv->viewport[3]; ++ z_2d = calculate_z_translation (perspective.z_near); ++ ++ /* NB: z_2d is only enough room for 85% of the stage_height between ++ * the stage and the z_near plane. For behind the stage plane we ++ * want a more consistent gap of 10 times the stage_height before ++ * hitting the far plane so we calculate that relative to the final ++ * height of the stage plane at the z_2d_distance we got... */ ++ perspective.z_far = z_2d + ++ tanf (_DEG_TO_RAD (perspective.fovy / 2.0f)) * z_2d * 20.0f; ++ ++ clutter_stage_set_perspective_internal (stage, &perspective); ++ } ++ else ++ { ++ z_2d = calculate_z_translation (perspective.z_near); ++ } ++ ++ cogl_matrix_init_identity (&priv->view); ++ cogl_matrix_view_2d_in_perspective (&priv->view, ++ perspective.fovy, ++ perspective.aspect, ++ perspective.z_near, ++ z_2d, ++ priv->viewport[2], ++ priv->viewport[3]); ++} ++ + void + _clutter_stage_maybe_setup_viewport (ClutterStage *stage, + ClutterStageView *view) +@@ -3797,7 +3821,6 @@ _clutter_stage_maybe_setup_viewport (ClutterStage *stage, + if (clutter_stage_view_is_dirty_viewport (view)) + { + cairo_rectangle_int_t view_layout; +- ClutterPerspective perspective; + float fb_scale; + float viewport_offset_x; + float viewport_offset_y; +@@ -3805,7 +3828,6 @@ _clutter_stage_maybe_setup_viewport (ClutterStage *stage, + float viewport_y; + float viewport_width; + float viewport_height; +- float z_2d; + + CLUTTER_NOTE (PAINT, + "Setting up the viewport { w:%f, h:%f }", +@@ -3825,38 +3847,6 @@ _clutter_stage_maybe_setup_viewport (ClutterStage *stage, + clutter_stage_view_set_viewport (view, + viewport_x, viewport_y, + viewport_width, viewport_height); +- +- perspective = priv->perspective; +- +- /* Ideally we want to regenerate the perspective matrix whenever +- * the size changes but if the user has provided a custom matrix +- * then we don't want to override it */ +- if (!priv->has_custom_perspective) +- { +- perspective.aspect = priv->viewport[2] / priv->viewport[3]; +- z_2d = calculate_z_translation (perspective.z_near); +- +- /* NB: z_2d is only enough room for 85% of the stage_height between +- * the stage and the z_near plane. For behind the stage plane we +- * want a more consistent gap of 10 times the stage_height before +- * hitting the far plane so we calculate that relative to the final +- * height of the stage plane at the z_2d_distance we got... */ +- perspective.z_far = z_2d + +- tanf (_DEG_TO_RAD (perspective.fovy / 2.0f)) * z_2d * 20.0f; +- +- clutter_stage_set_perspective_internal (stage, &perspective); +- } +- else +- z_2d = calculate_z_translation (perspective.z_near); +- +- cogl_matrix_init_identity (&priv->view); +- cogl_matrix_view_2d_in_perspective (&priv->view, +- perspective.fovy, +- perspective.aspect, +- perspective.z_near, +- z_2d, +- priv->viewport[2], +- priv->viewport[3]); + } + + if (clutter_stage_view_is_dirty_projection (view)) +-- +2.29.2 + diff --git a/SOURCES/wayland-frame-callback-rework.patch b/SOURCES/wayland-frame-callback-rework.patch new file mode 100644 index 0000000..0a6717f --- /dev/null +++ b/SOURCES/wayland-frame-callback-rework.patch @@ -0,0 +1,1045 @@ +From 4232f2056cd31811d047104d1223f3e5ced4b107 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Thu, 16 Apr 2020 19:42:03 +0200 +Subject: [PATCH 1/5] clutter/stage: Make clutter_stage_schedule_update() + always schedule + +We could call clutter_stage_schedule_update() and it wouldn't actually +schedule anything, as the master frame clock only tries to reschedule if +1) there is an active timeline, 2) there are pending relayouts, 3) there +are pending redraws, or 4) there are pending events. Thus, a call to +clutter_stage_schedule_update() didn't have any effect if it was called +at the wrong time. + +Fix this by adding a boolean state "needs_update" to the stage, set on +clutter_stage_schedule_update() and cleared on +_clutter_stage_do_update(), that will make the master clock reschedule +an update if it is TRUE. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1218 +(cherry picked from commit b8003807b0772e97354302b5cc2825e0b22c6c83) +--- + clutter/clutter/clutter-stage.c | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c +index aaa77d9ede..5a914a3d0b 100644 +--- a/clutter/clutter/clutter-stage.c ++++ b/clutter/clutter/clutter-stage.c +@@ -152,6 +152,8 @@ struct _ClutterStagePrivate + + int update_freeze_count; + ++ gboolean needs_update; ++ + guint relayout_pending : 1; + guint redraw_pending : 1; + guint is_fullscreen : 1; +@@ -1074,7 +1076,9 @@ _clutter_stage_needs_update (ClutterStage *stage) + + priv = stage->priv; + +- return priv->relayout_pending || priv->redraw_pending; ++ return priv->relayout_pending || ++ priv->needs_update || ++ priv->redraw_pending; + } + + void +@@ -1232,6 +1236,8 @@ _clutter_stage_do_update (ClutterStage *stage) + + priv->stage_was_relayout = FALSE; + ++ priv->needs_update = FALSE; ++ + /* if the stage is being destroyed, or if the destruction already + * happened and we don't have an StageWindow any more, then we + * should bail out +@@ -4080,6 +4086,8 @@ _clutter_stage_schedule_update (ClutterStage *stage) + if (stage_window == NULL) + return; + ++ stage->priv->needs_update = TRUE; ++ + return _clutter_stage_window_schedule_update (stage_window, + stage->priv->sync_delay); + } +-- +2.31.1 + + +From 2d4453cfdcaf0c6a9f492d2c2395dbfc8cf833db Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Thu, 16 Apr 2020 19:11:37 +0200 +Subject: [PATCH 2/5] clutter/stage: Make clutter_stage_schedule_update() + public API + +It's effectively used by mutter by abusing a ClutterTimeline to scedule +updates. Timelines are not really suited in places that is done, as it +is really just about getting a single new update scheduled whenever +suitable, so expose the API so we can use it directly. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1218 +(cherry picked from commit 99c9a14bc8058830232cd4e07c7bb897e84a8c9c) +--- + clutter/clutter/clutter-master-clock-default.c | 4 ++-- + clutter/clutter/clutter-stage-private.h | 1 - + clutter/clutter/clutter-stage.c | 16 ++++++++-------- + clutter/clutter/clutter-stage.h | 3 +++ + 4 files changed, 13 insertions(+), 11 deletions(-) + +diff --git a/clutter/clutter/clutter-master-clock-default.c b/clutter/clutter/clutter-master-clock-default.c +index 0647c3a7fd..de7a1e2f4d 100644 +--- a/clutter/clutter/clutter-master-clock-default.c ++++ b/clutter/clutter/clutter-master-clock-default.c +@@ -206,7 +206,7 @@ master_clock_schedule_stage_updates (ClutterMasterClockDefault *master_clock) + stages = clutter_stage_manager_peek_stages (stage_manager); + + for (l = stages; l != NULL; l = l->next) +- _clutter_stage_schedule_update (l->data); ++ clutter_stage_schedule_update (l->data); + } + + static GSList * +@@ -259,7 +259,7 @@ master_clock_reschedule_stage_updates (ClutterMasterClockDefault *master_clock, + if (master_clock->timelines || + _clutter_stage_has_queued_events (l->data) || + _clutter_stage_needs_update (l->data)) +- _clutter_stage_schedule_update (l->data); ++ clutter_stage_schedule_update (l->data); + } + } + +diff --git a/clutter/clutter/clutter-stage-private.h b/clutter/clutter/clutter-stage-private.h +index 42474687ad..ea0ce5ec72 100644 +--- a/clutter/clutter/clutter-stage-private.h ++++ b/clutter/clutter/clutter-stage-private.h +@@ -70,7 +70,6 @@ void _clutter_stage_queue_event (ClutterStage *stage, + gboolean _clutter_stage_has_queued_events (ClutterStage *stage); + void _clutter_stage_process_queued_events (ClutterStage *stage); + void _clutter_stage_update_input_devices (ClutterStage *stage); +-void _clutter_stage_schedule_update (ClutterStage *stage); + gint64 _clutter_stage_get_update_time (ClutterStage *stage); + void _clutter_stage_clear_update_time (ClutterStage *stage); + gboolean _clutter_stage_has_full_redraw_queued (ClutterStage *stage); +diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c +index 5a914a3d0b..74ff8b1337 100644 +--- a/clutter/clutter/clutter-stage.c ++++ b/clutter/clutter/clutter-stage.c +@@ -921,7 +921,7 @@ _clutter_stage_queue_event (ClutterStage *stage, + { + ClutterMasterClock *master_clock = _clutter_master_clock_get_default (); + _clutter_master_clock_start_running (master_clock); +- _clutter_stage_schedule_update (stage); ++ clutter_stage_schedule_update (stage); + } + + /* if needed, update the state of the input device of the event. +@@ -1295,7 +1295,7 @@ clutter_stage_real_queue_relayout (ClutterActor *self) + + if (!priv->relayout_pending) + { +- _clutter_stage_schedule_update (stage); ++ clutter_stage_schedule_update (stage); + priv->relayout_pending = TRUE; + } + +@@ -3788,7 +3788,7 @@ clutter_stage_ensure_redraw (ClutterStage *stage) + priv = stage->priv; + + if (!priv->relayout_pending && !priv->redraw_pending) +- _clutter_stage_schedule_update (stage); ++ clutter_stage_schedule_update (stage); + + priv->relayout_pending = TRUE; + priv->redraw_pending = TRUE; +@@ -4069,13 +4069,13 @@ clutter_stage_get_minimum_size (ClutterStage *stage, + } + + /** +- * _clutter_stage_schedule_update: +- * @window: a #ClutterStage actor ++ * clutter_stage_schedule_update: ++ * @stage: a #ClutterStage actor + * + * Schedules a redraw of the #ClutterStage at the next optimal timestamp. + */ + void +-_clutter_stage_schedule_update (ClutterStage *stage) ++clutter_stage_schedule_update (ClutterStage *stage) + { + ClutterStageWindow *stage_window; + +@@ -4097,7 +4097,7 @@ _clutter_stage_schedule_update (ClutterStage *stage) + * @stage: a #ClutterStage actor + * + * Returns the earliest time in which the stage is ready to update. The update +- * time is set when _clutter_stage_schedule_update() is called. This can then ++ * time is set when clutter_stage_schedule_update() is called. This can then + * be used by e.g. the #ClutterMasterClock to know when the stage needs to be + * redrawn. + * +@@ -4267,7 +4267,7 @@ _clutter_stage_queue_actor_redraw (ClutterStage *stage, + + CLUTTER_NOTE (PAINT, "First redraw request"); + +- _clutter_stage_schedule_update (stage); ++ clutter_stage_schedule_update (stage); + priv->redraw_pending = TRUE; + + master_clock = _clutter_master_clock_get_default (); +diff --git a/clutter/clutter/clutter-stage.h b/clutter/clutter/clutter-stage.h +index 9da63d211d..53e37ae3bc 100644 +--- a/clutter/clutter/clutter-stage.h ++++ b/clutter/clutter/clutter-stage.h +@@ -265,6 +265,9 @@ CLUTTER_EXPORT + void clutter_stage_skip_sync_delay (ClutterStage *stage); + #endif + ++CLUTTER_EXPORT ++void clutter_stage_schedule_update (ClutterStage *stage); ++ + CLUTTER_EXPORT + gboolean clutter_stage_get_capture_final_size (ClutterStage *stage, + cairo_rectangle_int_t *rect, +-- +2.31.1 + + +From 1c4db591b6bb2ae9d649e8157eae24b875e7a22b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Mon, 28 Oct 2019 18:20:31 +0100 +Subject: [PATCH 3/5] wayland/actor-surface: Always store away frame callbacks + on commit + +We're expected by MetaWaylandSurface to always pick the frame callbacks +out from the pending state when committing (applying) so that no frame +callbacks are unaccounted for. We failed to do this if our actor for +some reason (e.g. associated window was unmanaged) was destroyed. To +handle this situation better, store away the frame callbacks until we +some later point in time need to pass them on forward. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/893 +--- + src/wayland/meta-wayland-actor-surface.c | 25 +++++++++++++++++++++++- + 1 file changed, 24 insertions(+), 1 deletion(-) + +diff --git a/src/wayland/meta-wayland-actor-surface.c b/src/wayland/meta-wayland-actor-surface.c +index 2471de0a92..264565c575 100644 +--- a/src/wayland/meta-wayland-actor-surface.c ++++ b/src/wayland/meta-wayland-actor-surface.c +@@ -35,6 +35,8 @@ typedef struct _MetaWaylandActorSurfacePrivate MetaWaylandActorSurfacePrivate; + struct _MetaWaylandActorSurfacePrivate + { + MetaSurfaceActor *actor; ++ ++ struct wl_list frame_callback_list; + }; + + G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (MetaWaylandActorSurface, +@@ -56,6 +58,7 @@ meta_wayland_actor_surface_dispose (GObject *object) + meta_wayland_actor_surface_get_instance_private (META_WAYLAND_ACTOR_SURFACE (object)); + MetaWaylandSurface *surface = + meta_wayland_surface_role_get_surface (META_WAYLAND_SURFACE_ROLE (object)); ++ MetaWaylandFrameCallback *cb, *next; + + if (priv->actor) + { +@@ -66,6 +69,9 @@ meta_wayland_actor_surface_dispose (GObject *object) + g_clear_object (&priv->actor); + } + ++ wl_list_for_each_safe (cb, next, &priv->frame_callback_list, link) ++ wl_resource_destroy (cb->resource); ++ + G_OBJECT_CLASS (meta_wayland_actor_surface_parent_class)->dispose (object); + } + +@@ -99,6 +105,9 @@ meta_wayland_actor_surface_queue_frame_callbacks (MetaWaylandActorSurface *actor + MetaSurfaceActorWayland *surface_actor_wayland = + META_SURFACE_ACTOR_WAYLAND (priv->actor); + ++ meta_surface_actor_wayland_add_frame_callbacks (surface_actor_wayland, ++ &priv->frame_callback_list); ++ wl_list_init (&priv->frame_callback_list); + meta_surface_actor_wayland_add_frame_callbacks (surface_actor_wayland, + &pending->frame_callback_list); + wl_list_init (&pending->frame_callback_list); +@@ -253,10 +262,20 @@ meta_wayland_actor_surface_commit (MetaWaylandSurfaceRole *surface_role, + { + MetaWaylandActorSurface *actor_surface = + META_WAYLAND_ACTOR_SURFACE (surface_role); ++ MetaWaylandActorSurfacePrivate *priv = ++ meta_wayland_actor_surface_get_instance_private (actor_surface); + MetaWaylandSurface *surface = + meta_wayland_surface_role_get_surface (surface_role); + MetaWaylandSurface *toplevel_surface; + ++ if (!priv->actor) ++ { ++ wl_list_insert_list (&priv->frame_callback_list, ++ &pending->frame_callback_list); ++ wl_list_init (&pending->frame_callback_list); ++ return; ++ } ++ + meta_wayland_actor_surface_queue_frame_callbacks (actor_surface, pending); + + toplevel_surface = meta_wayland_surface_get_toplevel (surface); +@@ -307,8 +326,12 @@ meta_wayland_actor_surface_is_on_logical_monitor (MetaWaylandSurfaceRole *surfac + } + + static void +-meta_wayland_actor_surface_init (MetaWaylandActorSurface *role) ++meta_wayland_actor_surface_init (MetaWaylandActorSurface *actor_surface) + { ++ MetaWaylandActorSurfacePrivate *priv = ++ meta_wayland_actor_surface_get_instance_private (actor_surface); ++ ++ wl_list_init (&priv->frame_callback_list); + } + + static void +-- +2.31.1 + + +From 54e5cee1a8d1a94fb19b438a3c80fe72179a3c80 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Fri, 6 Aug 2021 19:09:24 +0200 +Subject: [PATCH 4/5] wayland/actor-surface: Always store away frame callbacks + on commit + +We're expected by MetaWaylandSurface to always pick the frame callbacks +out from the pending state when committing (applying) so that no frame +callbacks are unaccounted for. We failed to do this if our actor for +some reason (e.g. associated window was unmanaged) was destroyed. To +handle this situation better, store away the frame callbacks until we +some later point in time need to pass them on forward. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/893 +--- + src/compositor/meta-surface-actor-wayland.c | 29 --------- + src/wayland/meta-wayland-actor-surface.c | 65 ++++++++++++++++----- + src/wayland/meta-wayland-actor-surface.h | 3 + + src/wayland/meta-wayland-cursor-surface.c | 4 +- + src/wayland/meta-wayland-dnd-surface.c | 11 +++- + src/wayland/meta-wayland-legacy-xdg-shell.c | 8 +-- + src/wayland/meta-wayland-private.h | 2 +- + src/wayland/meta-wayland-subsurface.c | 2 - + src/wayland/meta-wayland-surface.c | 38 +++--------- + src/wayland/meta-wayland-surface.h | 9 +-- + src/wayland/meta-wayland-wl-shell.c | 3 - + src/wayland/meta-wayland-xdg-shell.c | 15 ++--- + src/wayland/meta-wayland.c | 56 +++++++++++++----- + src/wayland/meta-wayland.h | 8 ++- + src/wayland/meta-xwayland.c | 45 -------------- + 15 files changed, 127 insertions(+), 171 deletions(-) + +diff --git a/src/compositor/meta-surface-actor-wayland.c b/src/compositor/meta-surface-actor-wayland.c +index a75c4dd096..480b51c61a 100644 +--- a/src/compositor/meta-surface-actor-wayland.c ++++ b/src/compositor/meta-surface-actor-wayland.c +@@ -42,7 +42,6 @@ struct _MetaSurfaceActorWayland + MetaSurfaceActor parent; + + MetaWaylandSurface *surface; +- struct wl_list frame_callback_list; + }; + + G_DEFINE_TYPE (MetaSurfaceActorWayland, +@@ -91,13 +90,6 @@ meta_surface_actor_wayland_is_unredirected (MetaSurfaceActor *actor) + return FALSE; + } + +-void +-meta_surface_actor_wayland_add_frame_callbacks (MetaSurfaceActorWayland *self, +- struct wl_list *frame_callbacks) +-{ +- wl_list_insert_list (&self->frame_callback_list, frame_callbacks); +-} +- + static MetaWindow * + meta_surface_actor_wayland_get_window (MetaSurfaceActor *actor) + { +@@ -158,22 +150,6 @@ meta_surface_actor_wayland_get_preferred_height (ClutterActor *actor, + *natural_height_p *= scale; + } + +-static void +-meta_surface_actor_wayland_paint (ClutterActor *actor) +-{ +- MetaSurfaceActorWayland *self = META_SURFACE_ACTOR_WAYLAND (actor); +- +- if (self->surface) +- { +- MetaWaylandCompositor *compositor = self->surface->compositor; +- +- wl_list_insert_list (&compositor->frame_callbacks, &self->frame_callback_list); +- wl_list_init (&self->frame_callback_list); +- } +- +- CLUTTER_ACTOR_CLASS (meta_surface_actor_wayland_parent_class)->paint (actor); +-} +- + static void + meta_surface_actor_wayland_dispose (GObject *object) + { +@@ -190,9 +166,6 @@ meta_surface_actor_wayland_dispose (GObject *object) + self->surface = NULL; + } + +- wl_list_for_each_safe (cb, next, &self->frame_callback_list, link) +- wl_resource_destroy (cb->resource); +- + G_OBJECT_CLASS (meta_surface_actor_wayland_parent_class)->dispose (object); + } + +@@ -205,7 +178,6 @@ meta_surface_actor_wayland_class_init (MetaSurfaceActorWaylandClass *klass) + + actor_class->get_preferred_width = meta_surface_actor_wayland_get_preferred_width; + actor_class->get_preferred_height = meta_surface_actor_wayland_get_preferred_height; +- actor_class->paint = meta_surface_actor_wayland_paint; + + surface_actor_class->process_damage = meta_surface_actor_wayland_process_damage; + surface_actor_class->pre_paint = meta_surface_actor_wayland_pre_paint; +@@ -232,7 +204,6 @@ meta_surface_actor_wayland_new (MetaWaylandSurface *surface) + + g_assert (meta_is_wayland_compositor ()); + +- wl_list_init (&self->frame_callback_list); + self->surface = surface; + g_object_add_weak_pointer (G_OBJECT (self->surface), + (gpointer *) &self->surface); +diff --git a/src/wayland/meta-wayland-actor-surface.c b/src/wayland/meta-wayland-actor-surface.c +index 264565c575..037dd901ab 100644 +--- a/src/wayland/meta-wayland-actor-surface.c ++++ b/src/wayland/meta-wayland-actor-surface.c +@@ -84,16 +84,22 @@ meta_wayland_actor_surface_assigned (MetaWaylandSurfaceRole *surface_role) + meta_wayland_surface_role_get_surface (surface_role); + GList *l; + +- meta_surface_actor_wayland_add_frame_callbacks (META_SURFACE_ACTOR_WAYLAND (priv->actor), +- &surface->pending_frame_callback_list); +- wl_list_init (&surface->pending_frame_callback_list); +- + for (l = surface->subsurfaces; l; l = l->next) + { + ClutterActor *subsurface_actor = + CLUTTER_ACTOR (meta_wayland_surface_get_actor (l->data)); + clutter_actor_add_child (CLUTTER_ACTOR (priv->actor), subsurface_actor); + } ++ ++ if (wl_list_empty (&surface->unassigned.pending_frame_callback_list)) ++ return; ++ ++ wl_list_insert_list (priv->frame_callback_list.prev, ++ &surface->unassigned.pending_frame_callback_list); ++ wl_list_init (&surface->unassigned.pending_frame_callback_list); ++ ++ meta_wayland_compositor_add_frame_callback_surface (surface->compositor, ++ surface); + } + + void +@@ -102,15 +108,40 @@ meta_wayland_actor_surface_queue_frame_callbacks (MetaWaylandActorSurface *actor + { + MetaWaylandActorSurfacePrivate *priv = + meta_wayland_actor_surface_get_instance_private (actor_surface); +- MetaSurfaceActorWayland *surface_actor_wayland = +- META_SURFACE_ACTOR_WAYLAND (priv->actor); ++ MetaWaylandSurfaceRole *surface_role = ++ META_WAYLAND_SURFACE_ROLE (actor_surface); ++ MetaWaylandSurface *surface = ++ meta_wayland_surface_role_get_surface (surface_role); + +- meta_surface_actor_wayland_add_frame_callbacks (surface_actor_wayland, +- &priv->frame_callback_list); +- wl_list_init (&priv->frame_callback_list); +- meta_surface_actor_wayland_add_frame_callbacks (surface_actor_wayland, +- &pending->frame_callback_list); ++ if (!priv->actor) ++ return; ++ ++ if (wl_list_empty (&pending->frame_callback_list)) ++ return; ++ ++ wl_list_insert_list (priv->frame_callback_list.prev, ++ &pending->frame_callback_list); + wl_list_init (&pending->frame_callback_list); ++ ++ meta_wayland_compositor_add_frame_callback_surface (surface->compositor, ++ surface); ++} ++ ++void ++meta_wayland_actor_surface_emit_frame_callbacks (MetaWaylandActorSurface *actor_surface, ++ uint32_t timestamp_ms) ++{ ++ MetaWaylandActorSurfacePrivate *priv = ++ meta_wayland_actor_surface_get_instance_private (actor_surface); ++ ++ while (!wl_list_empty (&priv->frame_callback_list)) ++ { ++ MetaWaylandFrameCallback *callback = ++ wl_container_of (priv->frame_callback_list.next, callback, link); ++ ++ wl_callback_send_done (callback->resource, timestamp_ms); ++ wl_resource_destroy (callback->resource); ++ } + } + + static double +@@ -268,12 +299,14 @@ meta_wayland_actor_surface_commit (MetaWaylandSurfaceRole *surface_role, + meta_wayland_surface_role_get_surface (surface_role); + MetaWaylandSurface *toplevel_surface; + +- if (!priv->actor) ++ if (!wl_list_empty (&pending->frame_callback_list) && ++ priv->actor && ++ !meta_surface_actor_is_obscured (priv->actor)) + { +- wl_list_insert_list (&priv->frame_callback_list, +- &pending->frame_callback_list); +- wl_list_init (&pending->frame_callback_list); +- return; ++ MetaBackend *backend = meta_get_backend (); ++ ClutterActor *stage = meta_backend_get_stage (backend); ++ ++ clutter_stage_schedule_update (CLUTTER_STAGE (stage)); + } + + meta_wayland_actor_surface_queue_frame_callbacks (actor_surface, pending); +diff --git a/src/wayland/meta-wayland-actor-surface.h b/src/wayland/meta-wayland-actor-surface.h +index 444b3b1785..e79f1caff5 100644 +--- a/src/wayland/meta-wayland-actor-surface.h ++++ b/src/wayland/meta-wayland-actor-surface.h +@@ -46,4 +46,7 @@ void meta_wayland_actor_surface_reset_actor (MetaWaylandActorSurface *actor_surf + void meta_wayland_actor_surface_queue_frame_callbacks (MetaWaylandActorSurface *actor_surface, + MetaWaylandPendingState *pending); + ++void meta_wayland_actor_surface_emit_frame_callbacks (MetaWaylandActorSurface *actor_surface, ++ uint32_t timestamp_ms); ++ + #endif /* META_WAYLAND_ACTOR_SURFACE_H */ +diff --git a/src/wayland/meta-wayland-cursor-surface.c b/src/wayland/meta-wayland-cursor-surface.c +index d46b3511fa..6b791eb378 100644 +--- a/src/wayland/meta-wayland-cursor-surface.c ++++ b/src/wayland/meta-wayland-cursor-surface.c +@@ -124,8 +124,8 @@ meta_wayland_cursor_surface_assigned (MetaWaylandSurfaceRole *surface_role) + meta_wayland_cursor_surface_get_instance_private (cursor_surface); + + wl_list_insert_list (&priv->frame_callbacks, +- &surface->pending_frame_callback_list); +- wl_list_init (&surface->pending_frame_callback_list); ++ &surface->unassigned.pending_frame_callback_list); ++ wl_list_init (&surface->unassigned.pending_frame_callback_list); + } + + static void +diff --git a/src/wayland/meta-wayland-dnd-surface.c b/src/wayland/meta-wayland-dnd-surface.c +index 2aad6dcd5d..8ddeb2a7bd 100644 +--- a/src/wayland/meta-wayland-dnd-surface.c ++++ b/src/wayland/meta-wayland-dnd-surface.c +@@ -21,6 +21,8 @@ + + #include "wayland/meta-wayland-dnd-surface.h" + ++#include "wayland/meta-wayland.h" ++ + struct _MetaWaylandSurfaceRoleDND + { + MetaWaylandActorSurface parent; +@@ -36,7 +38,11 @@ dnd_surface_assigned (MetaWaylandSurfaceRole *surface_role) + MetaWaylandSurface *surface = + meta_wayland_surface_role_get_surface (surface_role); + +- meta_wayland_surface_queue_pending_frame_callbacks (surface); ++ if (wl_list_empty (&surface->unassigned.pending_frame_callback_list)) ++ return; ++ ++ meta_wayland_compositor_add_frame_callback_surface (surface->compositor, ++ surface); + } + + static void +@@ -46,7 +52,8 @@ dnd_surface_commit (MetaWaylandSurfaceRole *surface_role, + MetaWaylandSurface *surface = + meta_wayland_surface_role_get_surface (surface_role); + +- meta_wayland_surface_queue_pending_state_frame_callbacks (surface, pending); ++ meta_wayland_compositor_add_frame_callback_surface (surface->compositor, ++ surface); + } + + static void +diff --git a/src/wayland/meta-wayland-legacy-xdg-shell.c b/src/wayland/meta-wayland-legacy-xdg-shell.c +index 8230641770..b78552f31b 100644 +--- a/src/wayland/meta-wayland-legacy-xdg-shell.c ++++ b/src/wayland/meta-wayland-legacy-xdg-shell.c +@@ -659,6 +659,8 @@ meta_wayland_zxdg_toplevel_v6_commit (MetaWaylandSurfaceRole *surface_role, + META_WAYLAND_ZXDG_SURFACE_V6 (xdg_toplevel); + MetaWaylandZxdgSurfaceV6Private *xdg_surface_priv = + meta_wayland_zxdg_surface_v6_get_instance_private (xdg_surface); ++ MetaWaylandActorSurface *actor_surface = ++ META_WAYLAND_ACTOR_SURFACE (xdg_surface); + MetaWaylandSurfaceRoleClass *surface_role_class; + MetaWaylandSurface *surface = + meta_wayland_surface_role_get_surface (surface_role); +@@ -670,7 +672,7 @@ meta_wayland_zxdg_toplevel_v6_commit (MetaWaylandSurfaceRole *surface_role, + window = surface->window; + if (!window) + { +- meta_wayland_surface_cache_pending_frame_callbacks (surface, pending); ++ meta_wayland_actor_surface_queue_frame_callbacks (actor_surface, pending); + return; + } + +@@ -1220,14 +1222,10 @@ meta_wayland_zxdg_surface_v6_send_configure (MetaWaylandZxdgSurfaceV6 *xdg_surfa + static void + zxdg_surface_v6_destructor (struct wl_resource *resource) + { +- MetaWaylandSurface *surface = surface_from_xdg_surface_resource (resource); + MetaWaylandZxdgSurfaceV6 *xdg_surface = wl_resource_get_user_data (resource); + MetaWaylandZxdgSurfaceV6Private *priv = + meta_wayland_zxdg_surface_v6_get_instance_private (xdg_surface); + +- meta_wayland_compositor_destroy_frame_callbacks (surface->compositor, +- surface); +- + priv->shell_client->surfaces = g_list_remove (priv->shell_client->surfaces, + xdg_surface); + +diff --git a/src/wayland/meta-wayland-private.h b/src/wayland/meta-wayland-private.h +index 5bcb0ea4f9..215d0967f6 100644 +--- a/src/wayland/meta-wayland-private.h ++++ b/src/wayland/meta-wayland-private.h +@@ -67,7 +67,7 @@ struct _MetaWaylandCompositor + struct wl_display *wayland_display; + char *display_name; + GHashTable *outputs; +- struct wl_list frame_callbacks; ++ GList *frame_callback_surfaces; + + MetaXWaylandManager xwayland_manager; + +diff --git a/src/wayland/meta-wayland-subsurface.c b/src/wayland/meta-wayland-subsurface.c +index e0fa0a48b2..c7059b99a2 100644 +--- a/src/wayland/meta-wayland-subsurface.c ++++ b/src/wayland/meta-wayland-subsurface.c +@@ -239,8 +239,6 @@ wl_subsurface_destructor (struct wl_resource *resource) + { + MetaWaylandSurface *surface = wl_resource_get_user_data (resource); + +- meta_wayland_compositor_destroy_frame_callbacks (surface->compositor, +- surface); + if (surface->sub.parent) + { + wl_list_remove (&surface->sub.parent_destroy_listener.link); +diff --git a/src/wayland/meta-wayland-surface.c b/src/wayland/meta-wayland-surface.c +index 6ffcd6a7fb..a76ab28c24 100644 +--- a/src/wayland/meta-wayland-surface.c ++++ b/src/wayland/meta-wayland-surface.c +@@ -358,15 +358,6 @@ surface_process_damage (MetaWaylandSurface *surface, + cairo_region_destroy (transformed_region); + } + +-void +-meta_wayland_surface_queue_pending_state_frame_callbacks (MetaWaylandSurface *surface, +- MetaWaylandPendingState *pending) +-{ +- wl_list_insert_list (&surface->compositor->frame_callbacks, +- &pending->frame_callback_list); +- wl_list_init (&pending->frame_callback_list); +-} +- + void + meta_wayland_surface_destroy_window (MetaWaylandSurface *surface) + { +@@ -656,15 +647,6 @@ parent_surface_state_applied (gpointer data, + meta_wayland_subsurface_parent_state_applied (subsurface); + } + +-void +-meta_wayland_surface_cache_pending_frame_callbacks (MetaWaylandSurface *surface, +- MetaWaylandPendingState *pending) +-{ +- wl_list_insert_list (&surface->pending_frame_callback_list, +- &pending->frame_callback_list); +- wl_list_init (&pending->frame_callback_list); +-} +- + void + meta_wayland_surface_apply_pending_state (MetaWaylandSurface *surface, + MetaWaylandPendingState *pending) +@@ -810,7 +792,9 @@ meta_wayland_surface_apply_pending_state (MetaWaylandSurface *surface, + } + else + { +- meta_wayland_surface_cache_pending_frame_callbacks (surface, pending); ++ wl_list_insert_list (surface->unassigned.pending_frame_callback_list.prev, ++ &pending->frame_callback_list); ++ wl_list_init (&pending->frame_callback_list); + + if (pending->newly_attached) + { +@@ -1352,12 +1336,14 @@ wl_surface_destructor (struct wl_resource *resource) + if (surface->input_region) + cairo_region_destroy (surface->input_region); + +- meta_wayland_compositor_destroy_frame_callbacks (compositor, surface); ++ meta_wayland_compositor_remove_frame_callback_surface (compositor, surface); + + g_hash_table_foreach (surface->outputs_to_destroy_notify_id, surface_output_disconnect_signal, surface); + g_hash_table_unref (surface->outputs_to_destroy_notify_id); + +- wl_list_for_each_safe (cb, next, &surface->pending_frame_callback_list, link) ++ wl_list_for_each_safe (cb, next, ++ &surface->unassigned.pending_frame_callback_list, ++ link) + wl_resource_destroy (cb->resource); + + if (surface->resource) +@@ -1401,7 +1387,7 @@ meta_wayland_surface_create (MetaWaylandCompositor *compositor, + surface->resource = wl_resource_create (client, &wl_surface_interface, wl_resource_get_version (compositor_resource), id); + wl_resource_set_implementation (surface->resource, &meta_wayland_wl_surface_interface, surface, wl_surface_destructor); + +- wl_list_init (&surface->pending_frame_callback_list); ++ wl_list_init (&surface->unassigned.pending_frame_callback_list); + + sync_drag_dest_funcs (surface); + +@@ -1809,14 +1795,6 @@ meta_wayland_surface_role_get_surface (MetaWaylandSurfaceRole *role) + return priv->surface; + } + +-void +-meta_wayland_surface_queue_pending_frame_callbacks (MetaWaylandSurface *surface) +-{ +- wl_list_insert_list (&surface->compositor->frame_callbacks, +- &surface->pending_frame_callback_list); +- wl_list_init (&surface->pending_frame_callback_list); +-} +- + cairo_region_t * + meta_wayland_surface_calculate_input_region (MetaWaylandSurface *surface) + { +diff --git a/src/wayland/meta-wayland-surface.h b/src/wayland/meta-wayland-surface.h +index e244a3fdf7..776431fca2 100644 +--- a/src/wayland/meta-wayland-surface.h ++++ b/src/wayland/meta-wayland-surface.h +@@ -160,13 +160,9 @@ struct _MetaWaylandSurface + /* Buffer renderer state. */ + gboolean buffer_held; + +- /* List of pending frame callbacks that needs to stay queued longer than one +- * commit sequence, such as when it has not yet been assigned a role. +- */ +- struct wl_list pending_frame_callback_list; +- + /* Intermediate state for when no role has been assigned. */ + struct { ++ struct wl_list pending_frame_callback_list; + MetaWaylandBuffer *buffer; + } unassigned; + +@@ -274,9 +270,6 @@ MetaWaylandSurface *meta_wayland_surface_get_toplevel (MetaWaylandSurface *surfa + + MetaWindow * meta_wayland_surface_get_toplevel_window (MetaWaylandSurface *surface); + +-void meta_wayland_surface_cache_pending_frame_callbacks (MetaWaylandSurface *surface, +- MetaWaylandPendingState *pending); +- + void meta_wayland_surface_queue_pending_frame_callbacks (MetaWaylandSurface *surface); + + void meta_wayland_surface_queue_pending_state_frame_callbacks (MetaWaylandSurface *surface, +diff --git a/src/wayland/meta-wayland-wl-shell.c b/src/wayland/meta-wayland-wl-shell.c +index 539fb9858e..e80db17e78 100644 +--- a/src/wayland/meta-wayland-wl-shell.c ++++ b/src/wayland/meta-wayland-wl-shell.c +@@ -100,9 +100,6 @@ wl_shell_surface_destructor (struct wl_resource *resource) + surface_from_wl_shell_surface_resource (resource); + GList *l; + +- meta_wayland_compositor_destroy_frame_callbacks (surface->compositor, +- surface); +- + if (wl_shell_surface->popup) + meta_wayland_popup_dismiss (wl_shell_surface->popup); + +diff --git a/src/wayland/meta-wayland-xdg-shell.c b/src/wayland/meta-wayland-xdg-shell.c +index fa0207a03c..4a4995c425 100644 +--- a/src/wayland/meta-wayland-xdg-shell.c ++++ b/src/wayland/meta-wayland-xdg-shell.c +@@ -684,6 +684,8 @@ meta_wayland_xdg_toplevel_commit (MetaWaylandSurfaceRole *surface_role, + MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (xdg_toplevel); + MetaWaylandXdgSurfacePrivate *xdg_surface_priv = + meta_wayland_xdg_surface_get_instance_private (xdg_surface); ++ MetaWaylandActorSurface *actor_surface = ++ META_WAYLAND_ACTOR_SURFACE (xdg_toplevel); + MetaWaylandSurfaceRoleClass *surface_role_class; + MetaWaylandSurface *surface = + meta_wayland_surface_role_get_surface (surface_role); +@@ -695,15 +697,12 @@ meta_wayland_xdg_toplevel_commit (MetaWaylandSurfaceRole *surface_role, + window = surface->window; + if (!window) + { +- meta_wayland_surface_cache_pending_frame_callbacks (surface, pending); ++ meta_wayland_actor_surface_queue_frame_callbacks (actor_surface, pending); + return; + } + + if (!surface->buffer_ref.buffer && xdg_surface_priv->first_buffer_attached) + { +- MetaWaylandActorSurface *actor_surface = +- META_WAYLAND_ACTOR_SURFACE (xdg_toplevel); +- + meta_wayland_xdg_surface_reset (xdg_surface); + meta_wayland_actor_surface_queue_frame_callbacks (actor_surface, + pending); +@@ -1037,6 +1036,8 @@ meta_wayland_xdg_popup_commit (MetaWaylandSurfaceRole *surface_role, + MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (surface_role); + MetaWaylandXdgSurfacePrivate *xdg_surface_priv = + meta_wayland_xdg_surface_get_instance_private (xdg_surface); ++ MetaWaylandActorSurface *actor_surface = ++ META_WAYLAND_ACTOR_SURFACE (xdg_popup); + MetaWaylandSurfaceRoleClass *surface_role_class; + MetaWaylandSurface *surface = + meta_wayland_surface_role_get_surface (surface_role); +@@ -1048,7 +1049,7 @@ meta_wayland_xdg_popup_commit (MetaWaylandSurfaceRole *surface_role, + if (!surface->buffer_ref.buffer && xdg_surface_priv->first_buffer_attached) + { + meta_wayland_xdg_surface_reset (xdg_surface); +- meta_wayland_surface_cache_pending_frame_callbacks (surface, pending); ++ meta_wayland_actor_surface_queue_frame_callbacks (actor_surface, pending); + return; + } + +@@ -1313,14 +1314,10 @@ meta_wayland_xdg_surface_send_configure (MetaWaylandXdgSurface *xdg_surface) + static void + xdg_surface_destructor (struct wl_resource *resource) + { +- MetaWaylandSurface *surface = surface_from_xdg_surface_resource (resource); + MetaWaylandXdgSurface *xdg_surface = wl_resource_get_user_data (resource); + MetaWaylandXdgSurfacePrivate *priv = + meta_wayland_xdg_surface_get_instance_private (xdg_surface); + +- meta_wayland_compositor_destroy_frame_callbacks (surface->compositor, +- surface); +- + priv->shell_client->surfaces = g_list_remove (priv->shell_client->surfaces, + xdg_surface); + +diff --git a/src/wayland/meta-wayland.c b/src/wayland/meta-wayland.c +index 129da8e20d..4cb9ca650d 100644 +--- a/src/wayland/meta-wayland.c ++++ b/src/wayland/meta-wayland.c +@@ -194,15 +194,35 @@ meta_wayland_compositor_update (MetaWaylandCompositor *compositor, + void + meta_wayland_compositor_paint_finished (MetaWaylandCompositor *compositor) + { +- gint64 current_time = g_get_monotonic_time (); ++ GList *l; ++ int64_t now_us; + +- while (!wl_list_empty (&compositor->frame_callbacks)) ++ now_us = g_get_monotonic_time (); ++ ++ l = compositor->frame_callback_surfaces; ++ while (l) + { +- MetaWaylandFrameCallback *callback = +- wl_container_of (compositor->frame_callbacks.next, callback, link); ++ GList *l_cur = l; ++ MetaWaylandSurface *surface = l->data; ++ MetaSurfaceActor *actor; ++ MetaWaylandActorSurface *actor_surface; ++ ++ l = l->next; ++ ++ actor = meta_wayland_surface_get_actor (surface); ++ if (!actor) ++ continue; ++ ++ if (!clutter_actor_has_mapped_clones (CLUTTER_ACTOR (actor)) && ++ meta_surface_actor_is_obscured (actor)) ++ continue; + +- wl_callback_send_done (callback->resource, current_time / 1000); +- wl_resource_destroy (callback->resource); ++ actor_surface = META_WAYLAND_ACTOR_SURFACE (surface->role); ++ meta_wayland_actor_surface_emit_frame_callbacks (actor_surface, ++ now_us / 1000); ++ ++ compositor->frame_callback_surfaces = ++ g_list_delete_link (compositor->frame_callback_surfaces, l_cur); + } + } + +@@ -249,16 +269,22 @@ meta_wayland_compositor_update_key_state (MetaWaylandCompositor *compositor, + } + + void +-meta_wayland_compositor_destroy_frame_callbacks (MetaWaylandCompositor *compositor, +- MetaWaylandSurface *surface) ++meta_wayland_compositor_add_frame_callback_surface (MetaWaylandCompositor *compositor, ++ MetaWaylandSurface *surface) + { +- MetaWaylandFrameCallback *callback, *next; ++ if (g_list_find (compositor->frame_callback_surfaces, surface)) ++ return; + +- wl_list_for_each_safe (callback, next, &compositor->frame_callbacks, link) +- { +- if (callback->surface == surface) +- wl_resource_destroy (callback->resource); +- } ++ compositor->frame_callback_surfaces = ++ g_list_prepend (compositor->frame_callback_surfaces, surface); ++} ++ ++void ++meta_wayland_compositor_remove_frame_callback_surface (MetaWaylandCompositor *compositor, ++ MetaWaylandSurface *surface) ++{ ++ compositor->frame_callback_surfaces = ++ g_list_remove (compositor->frame_callback_surfaces, surface); + } + + static void +@@ -309,8 +335,6 @@ meta_wayland_log_func (const char *fmt, + static void + meta_wayland_compositor_init (MetaWaylandCompositor *compositor) + { +- wl_list_init (&compositor->frame_callbacks); +- + compositor->scheduled_surface_associations = g_hash_table_new (NULL, NULL); + + wl_log_set_handler_server (meta_wayland_log_func); +diff --git a/src/wayland/meta-wayland.h b/src/wayland/meta-wayland.h +index 2a0aa11400..c5e5924891 100644 +--- a/src/wayland/meta-wayland.h ++++ b/src/wayland/meta-wayland.h +@@ -64,9 +64,11 @@ void meta_wayland_compositor_set_input_focus (MetaWaylandComp + META_EXPORT_TEST + void meta_wayland_compositor_paint_finished (MetaWaylandCompositor *compositor); + +-META_EXPORT_TEST +-void meta_wayland_compositor_destroy_frame_callbacks (MetaWaylandCompositor *compositor, +- MetaWaylandSurface *surface); ++void meta_wayland_compositor_add_frame_callback_surface (MetaWaylandCompositor *compositor, ++ MetaWaylandSurface *surface); ++ ++void meta_wayland_compositor_remove_frame_callback_surface (MetaWaylandCompositor *compositor, ++ MetaWaylandSurface *surface); + + META_EXPORT_TEST + const char *meta_wayland_get_wayland_display_name (MetaWaylandCompositor *compositor); +diff --git a/src/wayland/meta-xwayland.c b/src/wayland/meta-xwayland.c +index 275aeb78cb..6e4b9a8ffd 100644 +--- a/src/wayland/meta-xwayland.c ++++ b/src/wayland/meta-xwayland.c +@@ -788,49 +788,6 @@ meta_xwayland_stop (MetaXWaylandManager *manager) + } + } + +-static void +-xwayland_surface_assigned (MetaWaylandSurfaceRole *surface_role) +-{ +- MetaWaylandSurface *surface = +- meta_wayland_surface_role_get_surface (surface_role); +- MetaWaylandSurfaceRoleClass *surface_role_class = +- META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_surface_role_xwayland_parent_class); +- +- /* See comment in xwayland_surface_commit for why we reply even though the +- * surface may not be drawn the next frame. +- */ +- wl_list_insert_list (&surface->compositor->frame_callbacks, +- &surface->pending_frame_callback_list); +- wl_list_init (&surface->pending_frame_callback_list); +- +- surface_role_class->assigned (surface_role); +-} +- +-static void +-xwayland_surface_commit (MetaWaylandSurfaceRole *surface_role, +- MetaWaylandPendingState *pending) +-{ +- MetaWaylandSurface *surface = +- meta_wayland_surface_role_get_surface (surface_role); +- MetaWaylandSurfaceRoleClass *surface_role_class = +- META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_surface_role_xwayland_parent_class); +- +- /* For Xwayland windows, throttling frames when the window isn't actually +- * drawn is less useful, because Xwayland still has to do the drawing sent +- * from the application - the throttling would only be of sending us damage +- * messages, so we simplify and send frame callbacks after the next paint of +- * the screen, whether the window was drawn or not. +- * +- * Currently it may take a few frames before we draw the window, for not +- * completely understood reasons, and in that case, not thottling frame +- * callbacks to drawing has the happy side effect that we avoid showing the +- * user the initial black frame from when the window is mapped empty. +- */ +- meta_wayland_surface_queue_pending_state_frame_callbacks (surface, pending); +- +- surface_role_class->commit (surface_role, pending); +-} +- + static MetaWaylandSurface * + xwayland_surface_get_toplevel (MetaWaylandSurfaceRole *surface_role) + { +@@ -848,8 +805,6 @@ meta_wayland_surface_role_xwayland_class_init (MetaWaylandSurfaceRoleXWaylandCla + MetaWaylandSurfaceRoleClass *surface_role_class = + META_WAYLAND_SURFACE_ROLE_CLASS (klass); + +- surface_role_class->assigned = xwayland_surface_assigned; +- surface_role_class->commit = xwayland_surface_commit; + surface_role_class->get_toplevel = xwayland_surface_get_toplevel; + + xwayland_surface_signals[XWAYLAND_SURFACE_WINDOW_ASSOCIATED] = +-- +2.31.1 + + +From 076ac20d34db128aea8ffe0dc3c2791918667c43 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Fri, 6 Aug 2021 19:46:06 +0200 +Subject: [PATCH 5/5] wayland: Respond to frame callbacks even if the paint was + empty + +--- + src/compositor/compositor.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c +index ce2c1b8a3b..8331737d1a 100644 +--- a/src/compositor/compositor.c ++++ b/src/compositor/compositor.c +@@ -467,11 +467,6 @@ after_stage_paint (ClutterStage *stage, + + for (l = compositor->windows; l; l = l->next) + meta_window_actor_post_paint (l->data); +- +-#ifdef HAVE_WAYLAND +- if (meta_is_wayland_compositor ()) +- meta_wayland_compositor_paint_finished (meta_wayland_compositor_get_default ()); +-#endif + } + + static void +@@ -1404,6 +1399,11 @@ meta_post_paint_func (gpointer data) + break; + } + ++#ifdef HAVE_WAYLAND ++ if (meta_is_wayland_compositor ()) ++ meta_wayland_compositor_paint_finished (meta_wayland_compositor_get_default ()); ++#endif ++ + return TRUE; + } + +-- +2.31.1 + diff --git a/SOURCES/xwayland-xauth-xhost-user.patch b/SOURCES/xwayland-xauth-xhost-user.patch new file mode 100644 index 0000000..519bb3b --- /dev/null +++ b/SOURCES/xwayland-xauth-xhost-user.patch @@ -0,0 +1,479 @@ +From 8e756d48ed31bcacf12b99cbd82fb2052503f51e Mon Sep 17 00:00:00 2001 +From: Hans de Goede +Date: Tue, 18 Jun 2019 16:12:46 +0200 +Subject: [PATCH 1/4] xwayland: Generate a Xauth file and pass this to Xwayland + when starting it + +Before this commit, sudo x11-app, e.g. sudo gvim /etc/some-file, fails +when running a Wayland session. Where as doing this under a "GNOME on Xorg" +session works fine. For a user switching from the Xorg session to the +Wayland session, this is regression, which we want to avoid. + +This commit fixes this by creating and passing an xauth file to Xwayland when +mutter starts it. Just like gdm or startx pass a xauth file to Xorg when they +start Xorg. + +Fixes #643 + +https://gitlab.gnome.org/GNOME/mutter/issues/643 +--- + meson.build | 1 + + src/meson.build | 1 + + src/wayland/meta-wayland-private.h | 1 + + src/wayland/meta-wayland.c | 11 +++- + src/wayland/meta-xwayland.c | 81 ++++++++++++++++++++++++++++++ + 5 files changed, 94 insertions(+), 1 deletion(-) + +diff --git a/meson.build b/meson.build +index 8ef592bc58..2a404857ce 100644 +--- a/meson.build ++++ b/meson.build +@@ -117,6 +117,7 @@ xrandr_dep = dependency('xrandr', version: xrandr_req) + xcb_randr_dep = dependency('xcb-randr') + xcb_res_dep = dependency('xcb-res') + xinerama_dep = dependency('xinerama') ++xau_dep = dependency('xau') + ice_dep = dependency('ice') + atk_dep = dependency('atk', version: atk_req) + libcanberra_dep = dependency('libcanberra', version: libcanberra_req) +diff --git a/src/meson.build b/src/meson.build +index 7cced8f534..91fe74b99a 100644 +--- a/src/meson.build ++++ b/src/meson.build +@@ -101,6 +101,7 @@ if have_x11 + x11_xcb_dep, + xcb_randr_dep, + xcb_res_dep, ++ xau_dep, + ] + + if have_sm +diff --git a/src/wayland/meta-wayland-private.h b/src/wayland/meta-wayland-private.h +index 07a71f82b1..5bcb0ea4f9 100644 +--- a/src/wayland/meta-wayland-private.h ++++ b/src/wayland/meta-wayland-private.h +@@ -51,6 +51,7 @@ typedef struct + struct wl_client *client; + struct wl_resource *xserver_resource; + char *display_name; ++ char *auth_file; + + GCancellable *xserver_died_cancellable; + GSubprocess *proc; +diff --git a/src/wayland/meta-wayland.c b/src/wayland/meta-wayland.c +index a593f0a7b7..129da8e20d 100644 +--- a/src/wayland/meta-wayland.c ++++ b/src/wayland/meta-wayland.c +@@ -362,6 +362,12 @@ meta_wayland_override_display_name (const char *display_name) + _display_name_override = g_strdup (display_name); + } + ++static const char * ++meta_wayland_get_xwayland_auth_file (MetaWaylandCompositor *compositor) ++{ ++ return compositor->xwayland_manager.auth_file; ++} ++ + void + meta_wayland_init (void) + { +@@ -439,7 +445,10 @@ meta_wayland_init (void) + } + + if (meta_should_autostart_x11_display ()) +- set_gnome_env ("DISPLAY", meta_wayland_get_xwayland_display_name (compositor)); ++ { ++ set_gnome_env ("DISPLAY", meta_wayland_get_xwayland_display_name (compositor)); ++ set_gnome_env ("XAUTHORITY", meta_wayland_get_xwayland_auth_file (compositor)); ++ } + + set_gnome_env ("WAYLAND_DISPLAY", meta_wayland_get_wayland_display_name (compositor)); + } +diff --git a/src/wayland/meta-xwayland.c b/src/wayland/meta-xwayland.c +index f3df9766ee..c883eb3d6f 100644 +--- a/src/wayland/meta-xwayland.c ++++ b/src/wayland/meta-xwayland.c +@@ -32,6 +32,9 @@ + #include + #include + #include ++#include ++#include ++#include + + #include "compositor/meta-surface-actor-wayland.h" + #include "meta/main.h" +@@ -525,6 +528,75 @@ choose_xdisplay (MetaXWaylandManager *manager) + return TRUE; + } + ++G_DEFINE_AUTOPTR_CLEANUP_FUNC (FILE, fclose) ++ ++static gboolean ++prepare_auth_file (MetaXWaylandManager *manager) ++{ ++ Xauth auth_entry = { 0 }; ++ g_autoptr (FILE) fp = NULL; ++ char hostname[HOST_NAME_MAX + 1]; ++ char auth_data[16]; ++ int fd; ++ ++ manager->auth_file = g_build_filename (g_get_user_runtime_dir (), ++ ".mutter-Xwaylandauth.XXXXXX", ++ NULL); ++ ++ if (gethostname (hostname, HOST_NAME_MAX) < 0) ++ g_strlcpy (hostname, "localhost", HOST_NAME_MAX); ++ ++ if (getrandom (auth_data, sizeof (auth_data), 0) != sizeof (auth_data)) ++ { ++ g_warning ("Failed to get random data: %s", g_strerror (errno)); ++ return FALSE; ++ } ++ ++ auth_entry.family = FamilyLocal; ++ auth_entry.address = hostname; ++ auth_entry.address_length = strlen (auth_entry.address); ++ auth_entry.name = (char *) "MIT-MAGIC-COOKIE-1"; ++ auth_entry.name_length = strlen (auth_entry.name); ++ auth_entry.data = auth_data; ++ auth_entry.data_length = sizeof (auth_data); ++ ++ fd = g_mkstemp (manager->auth_file); ++ if (fd < 0) ++ { ++ g_warning ("Failed to open Xauthority file: %s", g_strerror (errno)); ++ return FALSE; ++ } ++ ++ fp = fdopen (fd, "w+"); ++ if (!fp) ++ { ++ g_warning ("Failed to open Xauthority stream: %s", g_strerror (errno)); ++ close (fd); ++ return FALSE; ++ } ++ ++ if (!XauWriteAuth (fp, &auth_entry)) ++ { ++ g_warning ("Error writing to Xauthority file: %s", g_strerror (errno)); ++ return FALSE; ++ } ++ ++ auth_entry.family = FamilyWild; ++ if (!XauWriteAuth (fp, &auth_entry)) ++ { ++ g_warning ("Error writing to Xauthority file: %s", g_strerror (errno)); ++ return FALSE; ++ } ++ ++ if (fflush (fp) == EOF) ++ { ++ g_warning ("Error writing to Xauthority file: %s", g_strerror (errno)); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ + static void + xserver_finished_init (MetaXWaylandManager *manager) + { +@@ -566,6 +638,9 @@ meta_xwayland_start (MetaXWaylandManager *manager, + if (!choose_xdisplay (manager)) + goto out; + ++ if (!prepare_auth_file (manager)) ++ goto out; ++ + /* We want xwayland to be a wayland client so we make a socketpair to setup a + * wayland protocol connection. */ + if (socketpair (AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, xwayland_client_fd) < 0) +@@ -610,6 +685,7 @@ meta_xwayland_start (MetaXWaylandManager *manager, + "-terminate", + "-accessx", + "-core", ++ "-auth", manager->auth_file, + "-listen", "4", + "-listen", "5", + "-displayfd", "6", +@@ -678,6 +754,11 @@ meta_xwayland_stop (MetaXWaylandManager *manager) + unlink (path); + + g_clear_pointer (&manager->display_name, g_free); ++ if (manager->auth_file) ++ { ++ unlink (manager->auth_file); ++ g_clear_pointer (&manager->auth_file, g_free); ++ } + if (manager->lock_file) + { + unlink (manager->lock_file); +-- +2.31.1 + + +From fdf6969cf89dc9127fc9f4d03d9408e54ccd1b40 Mon Sep 17 00:00:00 2001 +From: Olivier Fourdan +Date: Mon, 19 Aug 2019 15:36:32 +0200 +Subject: [PATCH 2/4] xwayland: pass the X11 display + +Pass the X11 display to `meta_xwayland_complete_init()` so that it can +be used without poking into GDK. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/735 +--- + src/wayland/meta-xwayland-private.h | 3 ++- + src/wayland/meta-xwayland.c | 3 ++- + src/x11/meta-x11-display.c | 5 ++--- + 3 files changed, 6 insertions(+), 5 deletions(-) + +diff --git a/src/wayland/meta-xwayland-private.h b/src/wayland/meta-xwayland-private.h +index 38874eda3f..abcb09e49b 100644 +--- a/src/wayland/meta-xwayland-private.h ++++ b/src/wayland/meta-xwayland-private.h +@@ -29,7 +29,8 @@ meta_xwayland_start (MetaXWaylandManager *manager, + struct wl_display *display); + + void +-meta_xwayland_complete_init (MetaDisplay *display); ++meta_xwayland_complete_init (MetaDisplay *display, ++ Display *xdisplay); + + void + meta_xwayland_stop (MetaXWaylandManager *manager); +diff --git a/src/wayland/meta-xwayland.c b/src/wayland/meta-xwayland.c +index c883eb3d6f..350626dfdb 100644 +--- a/src/wayland/meta-xwayland.c ++++ b/src/wayland/meta-xwayland.c +@@ -727,7 +727,8 @@ on_x11_display_closing (MetaDisplay *display) + + /* To be called right after connecting */ + void +-meta_xwayland_complete_init (MetaDisplay *display) ++meta_xwayland_complete_init (MetaDisplay *display, ++ Display *xdisplay) + { + /* We install an X IO error handler in addition to the child watch, + because after Xlib connects our child watch may not be called soon +diff --git a/src/x11/meta-x11-display.c b/src/x11/meta-x11-display.c +index 065ffcdda5..d40dcfa3f8 100644 +--- a/src/x11/meta-x11-display.c ++++ b/src/x11/meta-x11-display.c +@@ -1066,14 +1066,13 @@ meta_x11_display_new (MetaDisplay *display, GError **error) + + g_assert (prepared_gdk_display); + gdk_display = g_steal_pointer (&prepared_gdk_display); ++ xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display); + + #ifdef HAVE_WAYLAND + if (meta_is_wayland_compositor ()) +- meta_xwayland_complete_init (display); ++ meta_xwayland_complete_init (display, xdisplay); + #endif + +- xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display); +- + if (meta_is_syncing ()) + XSynchronize (xdisplay, True); + +-- +2.31.1 + + +From 25a0945aa69c479d6356a970b39e6ae42e43c877 Mon Sep 17 00:00:00 2001 +From: Olivier Fourdan +Date: Mon, 19 Aug 2019 15:48:17 +0200 +Subject: [PATCH 3/4] xwayland: Use given X11 display for DnD setup + +Use the provided X11 display instead of poking into GDK to get the X11 +display. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/735 +--- + src/wayland/meta-xwayland-private.h | 4 ++-- + src/wayland/meta-xwayland-selection.c | 18 +++++++++--------- + src/wayland/meta-xwayland.c | 7 +++++-- + 3 files changed, 16 insertions(+), 13 deletions(-) + +diff --git a/src/wayland/meta-xwayland-private.h b/src/wayland/meta-xwayland-private.h +index abcb09e49b..f562d7c96d 100644 +--- a/src/wayland/meta-xwayland-private.h ++++ b/src/wayland/meta-xwayland-private.h +@@ -36,8 +36,8 @@ void + meta_xwayland_stop (MetaXWaylandManager *manager); + + /* wl_data_device/X11 selection interoperation */ +-void meta_xwayland_init_selection (void); +-void meta_xwayland_shutdown_selection (void); ++void meta_xwayland_init_selection (Display *xdisplay); ++void meta_xwayland_shutdown_selection (Display *xdisplay); + gboolean meta_xwayland_selection_handle_event (XEvent *xevent); + + const MetaWaylandDragDestFuncs * meta_xwayland_selection_get_drag_dest_funcs (void); +diff --git a/src/wayland/meta-xwayland-selection.c b/src/wayland/meta-xwayland-selection.c +index 808f913339..122bb76e1c 100644 +--- a/src/wayland/meta-xwayland-selection.c ++++ b/src/wayland/meta-xwayland-selection.c +@@ -353,9 +353,9 @@ xdnd_send_status (MetaXWaylandSelection *selection_data, + } + + static void +-meta_xwayland_init_dnd (MetaXWaylandManager *manager) ++meta_xwayland_init_dnd (MetaXWaylandManager *manager, ++ Display *xdisplay) + { +- Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); + MetaDndBridge *dnd = &manager->selection_data->dnd; + XSetWindowAttributes attributes; + guint32 i, version = XDND_VERSION; +@@ -382,12 +382,12 @@ meta_xwayland_init_dnd (MetaXWaylandManager *manager) + } + + static void +-meta_xwayland_shutdown_dnd (MetaXWaylandManager *manager) ++meta_xwayland_shutdown_dnd (MetaXWaylandManager *manager, ++ Display *xdisplay) + { + MetaDndBridge *dnd = &manager->selection_data->dnd; + +- XDestroyWindow (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), +- dnd->dnd_window); ++ XDestroyWindow (xdisplay, dnd->dnd_window); + dnd->dnd_window = None; + } + +@@ -1755,7 +1755,7 @@ shutdown_selection_bridge (MetaSelectionBridge *selection) + } + + void +-meta_xwayland_init_selection (void) ++meta_xwayland_init_selection (Display *xdisplay) + { + MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); + MetaXWaylandManager *manager = &compositor->xwayland_manager; +@@ -1764,7 +1764,7 @@ meta_xwayland_init_selection (void) + + manager->selection_data = g_slice_new0 (MetaXWaylandSelection); + +- meta_xwayland_init_dnd (manager); ++ meta_xwayland_init_dnd (manager, xdisplay); + init_selection_bridge (&manager->selection_data->clipboard, + gdk_x11_get_xatom_by_name ("CLIPBOARD"), + &compositor->seat->data_device.selection_ownership_signal); +@@ -1777,7 +1777,7 @@ meta_xwayland_init_selection (void) + } + + void +-meta_xwayland_shutdown_selection (void) ++meta_xwayland_shutdown_selection (Display *xdisplay) + { + MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); + MetaXWaylandManager *manager = &compositor->xwayland_manager; +@@ -1787,7 +1787,7 @@ meta_xwayland_shutdown_selection (void) + + g_clear_object (&selection->clipboard.source); + +- meta_xwayland_shutdown_dnd (manager); ++ meta_xwayland_shutdown_dnd (manager, xdisplay); + shutdown_selection_bridge (&selection->clipboard); + shutdown_selection_bridge (&selection->primary); + shutdown_selection_bridge (&selection->dnd.selection); +diff --git a/src/wayland/meta-xwayland.c b/src/wayland/meta-xwayland.c +index 350626dfdb..3236711482 100644 +--- a/src/wayland/meta-xwayland.c ++++ b/src/wayland/meta-xwayland.c +@@ -38,6 +38,7 @@ + + #include "compositor/meta-surface-actor-wayland.h" + #include "meta/main.h" ++#include "meta/meta-x11-display.h" + #include "wayland/meta-wayland-actor-surface.h" + + enum +@@ -722,7 +723,9 @@ out: + static void + on_x11_display_closing (MetaDisplay *display) + { +- meta_xwayland_shutdown_selection (); ++ Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display); ++ ++ meta_xwayland_shutdown_selection (xdisplay); + } + + /* To be called right after connecting */ +@@ -739,7 +742,7 @@ meta_xwayland_complete_init (MetaDisplay *display, + + g_signal_connect (display, "x11-display-closing", + G_CALLBACK (on_x11_display_closing), NULL); +- meta_xwayland_init_selection (); ++ meta_xwayland_init_selection (xdisplay); + } + + void +-- +2.31.1 + + +From a398699a53b9cc6efda4aa8abe0e3176bab80e92 Mon Sep 17 00:00:00 2001 +From: Olivier Fourdan +Date: Mon, 19 Aug 2019 15:50:54 +0200 +Subject: [PATCH 4/4] xwayland: Add local user to xhost + +With the addition of xauth support (commit a8984a81c), Xwayland would +rely only on the provided cookies for authentication. + +As a result, running an Xclient from another VT (hence without the +XAUTHORITY environment variable set) would result in an access denied. + +The same on X11 is granted because the local user is automatically +granted access to Xserver by the startup scripts. + +Add the local user to xhost at startup on Xwayland so that the user can +still run a client by setting the DISPLAY as long as it's the same user +on the same host. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/735 +--- + src/wayland/meta-xwayland.c | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + +diff --git a/src/wayland/meta-xwayland.c b/src/wayland/meta-xwayland.c +index 3236711482..275aeb78cb 100644 +--- a/src/wayland/meta-xwayland.c ++++ b/src/wayland/meta-xwayland.c +@@ -598,6 +598,23 @@ prepare_auth_file (MetaXWaylandManager *manager) + return TRUE; + } + ++static void ++add_local_user_to_xhost (Display *xdisplay) ++{ ++ XHostAddress host_entry; ++ XServerInterpretedAddress siaddr; ++ ++ siaddr.type = (char *) "localuser"; ++ siaddr.typelength = strlen (siaddr.type); ++ siaddr.value = (char *) g_get_user_name(); ++ siaddr.valuelength = strlen (siaddr.value); ++ ++ host_entry.family = FamilyServerInterpreted; ++ host_entry.address = (char *) &siaddr; ++ ++ XAddHost (xdisplay, &host_entry); ++} ++ + static void + xserver_finished_init (MetaXWaylandManager *manager) + { +@@ -743,6 +760,7 @@ meta_xwayland_complete_init (MetaDisplay *display, + g_signal_connect (display, "x11-display-closing", + G_CALLBACK (on_x11_display_closing), NULL); + meta_xwayland_init_selection (xdisplay); ++ add_local_user_to_xhost (xdisplay); + } + + void +-- +2.31.1 + diff --git a/SPECS/mutter.spec b/SPECS/mutter.spec index 45e87b0..ee389e0 100644 --- a/SPECS/mutter.spec +++ b/SPECS/mutter.spec @@ -8,7 +8,7 @@ Name: mutter Version: 3.32.2 -Release: 55%{?dist} +Release: 59%{?dist} Summary: Window and compositing manager based on Clutter License: GPLv2+ @@ -177,6 +177,18 @@ Patch508: 0001-monitor-config-manager-Handle-multiple-builtin-panel.patch Patch509: 0001-clutter-stage-view-Hide-double-buffered-shadowfb-beh.patch Patch510: 0002-cogl-gpu-info-Fix-software-acceleration-detection.patch +# Backport of geometric picking, improving performance and fixing picking +# 10bpc pixel formats (#1919467) +Patch511: geometric-picking.patch + +Patch520: 0001-clutter-Backport-of-touch-mode.patch + +# Backport passing -xauth and adding local user to xhost (#1949176) +Patch521: xwayland-xauth-xhost-user.patch + +# Backport fixes avoiding frozen partly off-screen clients (#1989035) +Patch522: wayland-frame-callback-rework.patch + BuildRequires: chrpath BuildRequires: pango-devel BuildRequires: startup-notification-devel @@ -318,6 +330,22 @@ desktop-file-validate %{buildroot}/%{_datadir}/applications/%{name}.desktop %{_datadir}/mutter-%{mutter_api_version}/tests %changelog +* Fri Aug 06 2021 Jonas Ådahl - 3.32.2-59 +- Backport fixes avoiding frozen partly off-screen clients + Resolves: #1989035 + +* Mon Jul 05 2021 Jonas Ådahl - 3.32.2-58 +- Backport xauth and xhost patches + Resolves: #1949176 + +* Mon Feb 22 2021 Carlos Garnacho - 3.32.2-57 +- Backport touch-mode + Resolves: #1833787 + +* Tue Feb 09 2021 Jonas Ådahl - 3.32.2-56 +- Backport geometric picking patches + Resolves: #1919467 + * Tue Feb 09 2021 Jonas Ådahl - 3.32.2-55 - Fix slow nouveau with llvmpipe Resolves: #1921151