Commit Graph

289 Commits

Author SHA1 Message Date
Bilal Elmoussaoui
8d038789f6 clutter: Remove deprecated macros usage
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3103>
2023-08-07 19:30:34 +00:00
Bilal Elmoussaoui
b852bbba47 cleanup: Stop translating nick/blurb for pspecs
As those strings are intended to be used by some UI but nothing uses
that in reality except GStreamer.
So drop them similar to what GTK did at
https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/4717

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3101>
2023-07-19 11:33:59 +00:00
Jonas Ådahl
f4b76727db stage-view: Add API to inhibit cursor overlay painting
This adds some plumbing to get the "default" paint flags for regular
stage painting, where one either wants to paint the overlay, or not.

If inhibited, the 'no-cursors' paint flag is used, otherwise the 'none'
flag. This will be used to allow having a per stage view hw cursor
state.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2777>
2023-07-17 23:42:38 +02:00
Jonas Ådahl
e917b7de43 clutter/stage: Add input-only grabs
An input only grab is a ClutterGrab on the stage that doesn't have an
explicit actor associated with it. This is useful for cases where event
should be captured as if focus was stolen to some mysterious place that
doesn't have anything in the scene graph that represents it.

Internally, it's implemented using a 0x0 sized actor attached directly
to the stage, and a clutter action that consumes the events. An
input-only grab takes a handler, user data and a destroy function for
the user data. These are handed to the ClutterAction, which handles the
actual event handling.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2628>
2023-07-14 22:23:45 +00:00
Bilal Elmoussaoui
3590640932 clutter: Drop unnused private IDPool APIs
Nothing uses them

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3100>
2023-07-14 13:48:49 +00:00
Carlos Garnacho
c01580cec6 clutter: Remove some unused code
The drag actors were practically unused, and became wholly
unused API with commit eb6e1f694a. There is no provision for
using this in the future, so drop this internal API.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3058>
2023-06-15 16:27:48 +00:00
Jonas Ådahl
49e6ce459c backend: Dispatch initial burst of events synchronously on init
This will consist of device-added events, meaning before init finishes,
we can derive some state that depends on the set of input devices
available on startup, such as cursor visibility.

This avoids cursor visibility switching between hidden and visibility
during startup.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3070>
2023-06-12 18:16:01 +00:00
Jonas Dreßler
261f516af4 clutter/stage: Avoid queueing stage updates when they're already queued
We're using clutter_stage_schedule_update() now from ClutterActor to kick
off the stage updating machinery when a redraw needs to happen.

This introduced a bunch of unnecessary calls to
clutter_stage_schedule_update() and thus
clutter_stage_view_schedule_update() when multiple actors request redraws
during the same stage update cycle, which is a very common case.

Cut off all those unnecessary calls by bailing out in
clutter_stage_schedule_update() when updates are already queued.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2679>
2023-06-06 11:41:00 +00:00
Jonas Dreßler
699e1b305b clutter/actor: Refactor redraw clip to be centered around ClutterActor
So far our logic for queueing redraws goes like this: Actor notices that it
needs to redraw -> actor tells stage that it needs to redraw via
clutter_stage_queue_actor_redraw() -> stage collects more and more redraws
into a QueueRedrawList before the actual stage update happens -> when
that happens, the stage collects the actual redraw clips from the actors via
clutter_actor_get_redraw_clip().

The logic behind this QueueRedrawList was that by storing a list of
redraw entries on the stage, way we can avoid traversing the whole actor
tree one more time to build the redraw clip before the stage update.

These days we have clutter_actor_finish_layout() though, which is basically
exactly that, a whole actor tree traversal that happens before every stage
update.

Since we have that now, we might as well get rid of the whole dance back and
forth between ClutterStage and ClutterActor, and simply merge the logic to
queue redraws into the finish-layout step.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2679>
2023-06-06 11:41:00 +00:00
Pascal Nowack
30a5178204 clutter/clutter-stage: Get rid of an unused variable warning
When building mutter with -Ddebug=false, a warning appears, that
`ClutterStagePrivate *priv` is unused.
Simply remove this variable and directly use `stage->priv` in
`CLUTTER_NOTE` to get rid of this warning.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3046>
2023-06-02 12:22:18 +02:00
Georges Basile Stavracas Neto
0e8356936c clutter/actor: Move root node to ClutterStage
For now, it goes the "easy" way of creating the root node and
immediately painting and destroying it. From now on, the main
root node is created only by ClutterStage itself.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3007>
2023-05-23 12:47:07 +00:00
msizanoen1
4048562961 stage: Keep queued actor redraw clip volumes separate
This aims to reduce the amount of pixels that have to be redrawed on the
screen on a clipped actor redraw in case using the union of two
different clips in a surface will substantially increase the redrawn
area.

This should not result in excessive memory consumption as callers of
`clutter_actor_queue_redraw_with_clip` are expected to ensure that the
redraw clip rectangles are adequately deduplicated.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2965>
2023-05-03 08:53:36 +00:00
Georges Basile Stavracas Neto
81c9c43d26 clutter/stage: Assign frames to paint context
When rendering through the 'paint-view' handler, assign the frame
to the paint context. Otherwise, when rendering outside of the
frame clock schedule, don't.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2804>
2023-04-07 12:19:23 +02:00
Carlos Garnacho
e58f716fb1 clutter: Pass source device onto crossing event generation machinery
For motion-induced crossing events, this will be the device that generated
the motion. For code-induced crossing events (e.g. grabs or actors disappearing)
this will be none.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2828>
2023-02-09 14:38:39 +01:00
Jonas Dreßler
debbd88f8c clutter/stage: Cancel parts of implicit grabs when ClutterGrabs happen
A ClutterGrab takes precedence over implicit grabs, so when one happens,
let's check which part of the implicit grab tree is inside the new
ClutterGrab. Cancel and remove the parts which aren't, and if nothing
is in there anymore, cancel the whole implicit grab.

Emitting crossing events correctly here is getting quite tricky:

- When the implicit grab didn't get cancelled by the ClutterGrab, we
simply want to emit all GRAB_NOTIFY crossings to the implicit grab, as
we do with all other crossings.

- When the implicit grab did get cancelled and the new ClutterGrab wants
to emit ENTER crossings, we want those to be emitted to the actual
targets, so cancel the implicit grab before emission.

- In the last case where the implicit grab did get cancelled and the new
ClutterGrab wants to emit LEAVE crossings, those should be emitted to
the implicit grab again, so we cancel the grab only after the emission
of those.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2342>
2023-02-03 12:10:22 +00:00
Jonas Dreßler
2dd851d9f4 clutter: Add a GRABS debug flag
Now that we have two kinds of grabs, the intricacies of event delivery
got slightly more complicated. So this seems like a good point to
introduce a new GRABS debug flag that gives an overview of which grabs
are currently in effect.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2342>
2023-02-03 12:10:22 +00:00
Jonas Dreßler
cf2d44d2c2 clutter: Notify ClutterActions about sequences that were cancelled
We're almost there, everything is in place to notify ClutterActions
about a sequence getting pulled away under its feet.

The only thing that's missing is the actual notification to actions now,
so let's do that.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2342>
2023-02-03 12:10:22 +00:00
Jonas Dreßler
b3de224406 clutter/stage: Allow claiming sequence outside of event handling context
Another baby step just like the last commit: This commit takes care of
the opposite case: An action handling a sequence event stops further
emission of events to actors.

Since sequences remain around for longer than the context of just a
single event, it makes sense to provide a way to "claim" those sequences
even when outside of event handling context, so introduce API for that.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2342>
2023-02-03 12:10:22 +00:00
Jonas Dreßler
6939bed55c clutter/stage: Make sequences exclusive to actors vs actions
As soon as any event of a sequence is handles/stopped during emission,
all actors and actions that would have gotten to see it afterwards have
a big problem: If that event was a TOUCH_END event, the actor/action is
forever going to think that this touch is still active.

For ClutterActions, we're going to handle this by introducing a way to
send them a notification when stuff like this happens.

As a baby step towards all that, make event emission exclusive to actors
as soon as any actor stopped an event.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2342>
2023-02-03 12:10:22 +00:00
Jonas Dreßler
bac66fcbc4 clutter: Implicitly grab on button and touch event sequences
We'll soon introduce a new gesture tracking framework which heavily
depends on ClutterActions seeing all events of a sequence. For this to
work, a larger change to event delivery is needed: Implicit grabbing of
all events for button and touch press->motion->release sequences to
ensure ClutterActions continue receiving events for the whole sequence.

This commit takes care of that: At the start of an event sequence we
collect all the event-handling actors and actions to a GArray that lives
in the  PointerDeviceEntry, and then deliver all events belonging to
that sequence to the same actors/actions until the sequence ends.

To avoid events getting pulled from under our feet when mutters event
filter returns CLUTTER_EVENT_STOP, this also introduces private API
(maybe_lost_implicit_grab()) on ClutterStage so that we can't end up
with stale sequences.

Note that this also slightly changes behavior when it comes to event
delivery to actions: Because we now store actions separated from their
actors, any action returning CLUTTER_EVENT_STOP now stops event
propagation immediately. That was different before, where we'd emit
events to all actions of the actor and only then stop propagation.

Note that this isn't handling ClutterGrabs correctly right now,
this will be a little tricky, so we'll take care of that in a future
commit.

To handle actors getting destroyed or unmapped during a grab, listen to
notify::grab on the deepmost actor in the implicit grab tree. This gives
us a notification when any actor inside the tree goes unmapped.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2342>
2023-02-03 12:10:22 +00:00
Jonas Dreßler
d795710a14 clutter: Move emitting events to ClutterActions to the stage
A fairly small refactor, move the emission of events to actions from
clutter_actor_event() to stage level.

We do this because in the future we'll need to know on stage level
whether events were handled by an actor or by an action.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2342>
2023-02-03 12:10:22 +00:00
Jonas Dreßler
4358f8da7c clutter: Reuse GPtrArray for event emission
_clutter_actor_handle_event() currently allocates a new GPtrArray on the
heap for every single event emission, let's avoid this by keeping an
array around in ClutterStage and reusing that.

This is moving the last few bits of event emission into ClutterStage,
which will be useful when we introduce implicit grabbing in subsequent
commits.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2342>
2023-02-03 12:10:22 +00:00
Jonas Dreßler
a62ae73478 clutter: Propagate PROXIMITY events up and down the tree
There's no real reason to keep those events exclusive to the stage, some
actors or actions might want to get notified about proximity events too,
so propagate them like any other event.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2342>
2023-02-03 12:10:22 +00:00
Jonas Dreßler
0f0466fa8e clutter: Make the stage the central instance emitting events
Right now and due to loads of refactorings lately, the event emission
paths are a bit cluttered (ha ha ha) around in Clutter. For example the
event target actor gets set in clutter-main.c, but event emission is
actually managed by ClutterStage these days.

Since we'll introduce implicit grabbing of touch/button-press sequences
soon, let's shuffle things around a bit to make that easier:

Move event emission to the stage, it now gets a ClutterEvent without any
extra context like the target actor from clutter-main. The stage then
looks up the target actor itself and emits the event to the appropriate
actors in the scenegraph. A special path is introduced for emitting
crossing events, because here the event-receiving actors don't follow
the "capture+bubble from pointer actor to grab actor" rule.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2342>
2023-02-03 12:10:22 +00:00
Jonas Ådahl
08b0e563d4 clutter: Pass 'ClutterFrame' in all stage update signals
That means before-update, prepare-paint, before-paint, paint-view, after-paint,
after-update. While yet to be used, it will be used as a transient frame
book keeping object, to maintain object and state that is only valid
during a frame dispatch.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2795>
2023-01-23 15:57:50 +01:00
Jonas Ådahl
a897542876 clutter/actor: Stop transitions in children when removing
When we remove a child, we stop its transitions (animations), but we
didn't stop animations on grand children. What we did, however, was to
clear the stage views of the grand children, and this caused a bunch of
orphaned transitions (ClutterTimeline) and accompanied warnings.

Make it so that if we stop transitions, and clear stage views, also stop
transitions for the grand children. Detached children don't have a way
to continue animating anyway, since they have no stage view (thus frame
clock) to be driven by.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2716>
2022-11-25 10:26:52 +00:00
Jonas Dreßler
fe01e423a3 clutter/stage: Always queue events in _clutter_stage_queue_event()
We've been sending all events to clients immediately for quite some time
now, so this is only really impacting the Clutter scene graph, not
clients anymore.

That makes this behavior a somewhat unnecessary optimization (it was
useful at the time it was added, but it's not anymore), which will only
make our lives harder when we actually expect an event to be queued
(eg. in tests), so remove it.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2697>
2022-11-22 20:31:42 +00:00
Carlos Garnacho
714f01048e clutter: Demote the new clutter_stage_grab() precondition check
There do indeed seem to be places in our own code that trigger grabs on
actors before they are realized. It was not the intention to change the
practical preconditions for GNOME 43, so make it an even lower minimum
that every caller ought to match: That the actor is attached to the stage.

Further constraining of these preconditions will have to wait until
branching for new development.

Fixes: 9c79c7234 (clutter: Only allow grabs to be created on realized actors)
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2670>
2022-10-18 17:57:42 +02:00
Carlos Garnacho
9c79c72341 clutter: Only allow grabs to be created on realized actors
The bare minimum that we can ask to an actor before creating a grab
on it is that it is realized (and thus, attached to the stage). Bail
out if that is not the case when creating a grab.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2669>
2022-10-18 12:16:37 +02:00
Jonas Dreßler
4fce0b974b clutter: Add source field to crossing events
The source field was removed from ClutterEvent with commit
b644ea1bce because the preferred way of
getting the event actor is now to use the device/sequence actor from the
stage directly.

With crossing events it's not that easy though, as crossing events
explicitly have a source and related actor that doesn't have to be the
same actor as the device actor. Since we kept around the "related" field
there anyway, let's also introduce a "source" field in
ClutterCrossingEvent and return that actor when get_source() is called
on a crossing event.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2551>
2022-08-24 22:43:54 +00:00
Corentin Noël
8fcee7a8a5 clutter/stage: Add nullable annotation to get_*_actor methods
Allows to know when a NULL ClutterActor is possibly being returned.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2559>
2022-08-10 10:47:51 +00:00
Bilal Elmoussaoui
a73ae93d59 clutter: Remove all the since annotations
Since they are all refer to the days clutter used to be a separate
library

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2441>
2022-08-02 09:52:22 +02:00
Bilal Elmoussaoui
c8e63866f0 clutter: Migrate to gi-docgen
- Drop all the private structs documentations
- Make use of gi-docgen items linking as much as possible
- Use markdown formatting for code snippets

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2441>
2022-08-02 09:52:22 +02:00
Carlos Garnacho
6dabdec10e clutter/stage: Add function to maybe invalidate pointers on an actor
The function that currently invalidates pointers over an specific actor
also asserts for the situations where this invalidation makes sense to
happen (i.e. the actor became unmapped, or non-reactive).

We want to have a function that is more forgiving, and that doesn't
enforce any guarantees about the pointer focus actually changing.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2532>
2022-07-26 14:18:13 +00:00
Jonas Ådahl
cecf4cd87c stage: Add 'prepare-frame' signal
This is a signal that will be emitted between the 'before-update' and
'before-paint'. It can be used to handle things when you know whether
there is an update, and you know whether a paint or not will happen, by
looking at the current damage.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2393>
2022-04-29 20:51:25 +00:00
Corentin Noël
7d3ff66724 clutter/stage: Remove unused nullable annotation
This annotation wasn't taken into account because of the colon, but shouldn't
be applied as there is no way a NULL ClutterGrab can be returned.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2382>
2022-04-29 08:25:31 +00:00
Jonas Dreßler
cfdca246f2 clutter/stage: Repick when pointer actor goes unmapped
I've overseen quite an important case in commit
98a5cb37d9: Repicking only when actors get
destroyed is not enough, we actually need to repick when actors go
hidden/unmapped.

While we could also listen to notify::mapped just like we listen to
notify::reactive, it seems better to avoid using property notifications
here due to the usage of g_object_freeze/thaw_notify() in ClutterActor.
It can lead to the stage receiving a notify::mapped with mapped = true
for a pointer actor, which really shouldn't happen (just like
notify::reactive with reactive = true shouldn't happen).

Fixes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/5124
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2333>
2022-04-22 15:54:03 +00:00
Jonas Dreßler
2aad56b949 clutter: Pass target actor of events to event filter functions
We'll need the additional context of which actor the event will be
emitted to in mutters event filter (see next commit), so pass that
target actor to the event filters that are installed.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2321>
2022-03-21 23:08:40 +00:00
Sebastian Keller
703d8a77f1 clutter: Mark clutter_stage_grab() return value as transfer full
The caller is expected to unref the returned grab and all C code already
does this, but grabs requested from js were resulting in the memory
getting leaked due to it being annotated as transfer none.

Fixes: https://gitlab.gnome.org/GNOME/mutter/-/issues/2189
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2345>
2022-03-21 21:25:28 +01:00
Carlos Garnacho
e736b04deb clutter: Remove "source" event union fields
These are no longer used, drop the last places that modify those
in event structs and remove them.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2311>
2022-03-04 10:58:28 +00:00
Carlos Garnacho
62bd0359a9 clutter: Add clutter_stage_get_event_actor() API call
This is a small helper to retrieve the actor that is currently
beneath the device/sequence, meant to replace clutter_event_get_source().

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2311>
2022-03-04 10:58:28 +00:00
Carlos Garnacho
d51469ea2a clutter: Move priv variable initialization after precondition checks
In case of misuse (e.g. passing NULL stage) this might result in crashes
before the precondition checks managed to kick in. Move this priv variable
initialization after these checks.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2099>
2022-01-29 00:37:14 +00:00
Carlos Garnacho
a840c039a0 core: Handle redirection changes in and out of ClutterGrab grabs
Wayland event processing and WM operations are themselves outside the
ClutterGrab loop so far. Until this is sorted out, these pieces of
event handling have got to learn to stay aside while there is a
ClutterGrab going on.

So, synchronize foci and other state when grabs come in or out, and
make it sure that Wayland event processing does not happen while
grabs happen.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2099>
2022-01-29 00:37:14 +00:00
Carlos Garnacho
3a79b7b066 clutter: Add docs and introspection annotations to grabs
Since we want these accessed from bindings this must be a boxed
type. This has the side effect of making ClutterGrab a refcounted
object, since we want to avoid JS from pointing to freed memory
and maybe causing crashes if misusing the object after dismiss.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2099>
2022-01-29 00:37:14 +00:00
Carlos Garnacho
c2d7e635b1 clutter: Drop clutter_stage_[gs]et_throttle_motion_events()
This setting stays at the default and is left untouched, drop this
external switch on event handling internals.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2099>
2022-01-29 00:37:14 +00:00
Carlos Garnacho
6144244201 clutter: Drop clutter_stage_[gs]et_motion_events_enabled()
This is (luckily!) unused, and it's inconvenient to have a toggle to
break the input model we are striving towards. Drop this function
and stick to the default behavior.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2099>
2022-01-29 00:37:14 +00:00
Carlos Garnacho
dbfde95c5c clutter: Do not check redraw area for pointer repicks
This looks like a relic of glReadPixels-based picking, the pointer
might well be outside redrawn areas, yet still require a device
update (e.g. in order to reflect the actor layout changes in the
"clear area" info).

Instead, always update all devices that are inside the view after
relayouts, the tracking on the need for that update is now done
on each ClutterStageView, instead of globally in the ClutterStage.

This theoretically fixes situations where pointers might miss
updating their "clear area" after the actor tree changed.

Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/2117
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2257>
2022-01-27 11:08:24 +00:00
Carlos Garnacho
7bfc472ad3 clutter: Add specific event flag to tag "grab notify" crossing events
These events may need some differentiation or special handling, so add
this event flag and set it in the relevant events.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2068>
2022-01-11 16:13:39 +00:00
Carlos Garnacho
eb3075074a clutter: Integrate ClutterSeat grabs into ClutterGrab
As ClutterGrab is a stack, the backend only cares about some grab
existing currently or not. Make it sure that we grab whenever we
go to >=1 grabs, and ungrab whenever we go to <1.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2068>
2022-01-11 16:13:39 +00:00
Carlos Garnacho
1713f791cb clutter: Carry accounting of grabs in the ClutterActors holding them
And make it required that actors must be mapped to hold a grab. These
grabs will be automatically undone when the actor is unmapped.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2068>
2022-01-11 16:13:39 +00:00