Commit Graph

29222 Commits

Author SHA1 Message Date
Jonas Dreßler
6c17aa66c6 clutter: Remove device/sequence entry when TOUCH_END get filtered out
Mutters event filter can prevent events from getting processed by
Clutter, this can also happen for TOUCH_END/CANCEL events. Processing
these events in Clutter is crucial for proper tracking of touch
sequences though, that's because Clutter adds a PointerDeviceEntry to
the stage on a TOUCH_BEGIN *before* going through the event filter, but
removes that entry on a TOUCH_END *after* going through the filter. So
Clutter really needs to see those TOUCH_END events, or else there will
be a stale PointerDeviceEntry on the ClutterStage.

Make sure those TOUCH_END/CANCEL events always get seen by Clutter by
removing the device entry immediately when those get filtered out.

Because there might still be events belonging to this sequence in the
event queue of the stage, we need to flush the queue before removing the
entry, too.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2350>
2022-04-23 18:53:33 +00:00
Carlos Garnacho
cd0c47a25a clutter: Shuffle handling of IM reset on button presses
Unfortunately we cannot do this generically since the target of the
button/touch press does matter, e.g. tapping on the OSK, or clicking
the IBus candidates window. These situations should not trigger a
reset.

So be more selective about the situations where button/touch presses
trigger an IM reset, in the case of ClutterText these are still clicks
inside the actor, for Wayland's text-input it is when clicking the
surface that has text_input focus.

For all other situations where clicking anywhere else might make
sense to trigger an IM reset are covered by the focus changing paths,
that also ensure a reset before changing focus between surfaces/actors.

Fixes: https://gitlab.gnome.org/GNOME/mutter/-/issues/1961
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2384>
2022-04-23 15:04:30 +02:00
Carlos Garnacho
39ff8d15e2 wayland: Reset ClutterInputFocus on focus changes
Focus changes should trigger an IM reset, as some engines do want
to maybe commit the preedit string before changing focus. In addition,
we do not want the preedit string to be able to move between
windows/applications.

Ensure that the commit string is committed when the IM deems so, and
ensure we send a .done event disntinct to the .leave event, so that
the client doesn't miss the commit.

Fixes: https://gitlab.gnome.org/GNOME/mutter/-/issues/2030
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2384>
2022-04-23 15:04:30 +02:00
Carlos Garnacho
b4952c1199 clutter: Reset ClutterInputFocus on focus_out
Focus changes should trigger an IM reset, as some engines do want
to maybe commit the preedit buffer before changing focus. Since
the preedit string is also cleared on reset(), we can do without
that explicit call.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2384>
2022-04-23 15:04:30 +02:00
Carlos Garnacho
a9a890164a clutter: Actively update ClutterText surrounding text on changes
Right now we have a bit of a mixed bag between an active model where
input foci set the surrounding text without being asked for (e.g.
wayland's text_input), and a passive model where the IM engines ask
for content.

Make ClutterText take the same side than text_input, so that dealing
with those is at least consistent.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2384>
2022-04-23 15:04:30 +02:00
Carlos Garnacho
b4cdf5e098 clutter: Fix ClutterText ::delete-surrounding IM implementation
The clutter_text_delete_text() function used underneath expects character
offsets for both start/end position. Fix the end position passed an offset
instead of that, and compesnate for the cursor position being always -1
when the caret is at the end of the string.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2384>
2022-04-23 15:04:30 +02: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 Ådahl
358df1c569 screen-cast/src: Always allow MemFd buffer types to be selected
Just because the PipeWire client can handle DMA buffers, doesnt mean we
should not advertise support for MemFd.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2383>
2022-04-22 12:08:04 +00:00
Jonas Ådahl
2b4a24132b screen-cast: Test allocate DMA buffer with implicit API
DMA buffers might be allocatable, but it doesn't mean the driver doesn't
fail when we try to allocate a buffer with an implicit modifier. Using
the proprietary NVIDIA driver for example, it will fail. Lets catch this
up front and avoid advertising DMA buffer support when we know it won't
work.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2383>
2022-04-22 12:08:04 +00:00
Carlos Garnacho
f5f49dfa46 wayland: Always acknowledge state changing .commit() events with .done()
As of currently, we only emit .done() on actual changes coming from the
ClutterInputMethod/ClutterInputFocus. With the recent changes in the
interpretation of serials, it becomes more important now that the
compositor acknowledges every .commit done by the client, in order to
keep them feeding future IM state updates.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2365>
2022-04-22 11:39:44 +00:00
Carlos Garnacho
be2a1d85a5 wayland: Always update preedit with text_input.done()
Compensate the protocol statelessness with our ClutterInputFocus
statefulness. This becomes more necessary now, since sending
consecutive .done() events is now considered acceptable behavior.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2365>
2022-04-22 11:39:44 +00:00
Sebastian Wick
43ae37a93e backends/native: Update cursor also when scaled or transformed
MetaCursorRendererNative only updates the cursor state when the
underlying texture changes. The cursor scale and transform do not
trigger updates. This results in wrong cursor orientations on rotated
displays. Use both texture changes and scale and transformation changes
to figure out when to update the cursor state.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2363>
2022-04-21 11:23:15 +00:00
Jonas Dreßler
6e458f9462 clutter: Pass events to pointer a11y before going through filters
We want all pointer events to be passed through the pointer a11y
processing before going through event filters: Once we go through event
filters, events might be dispatched to Wayland and get filtered out.

With the changes to immediately dispatch events to wayland, this changed
and the pointer a11y is now no longer seeing any events going to wayland
clients. Fix it by shuffling things around a bit and letting pointer
a11y take a peek at events earlier.

Fixes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/5192
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2332>
2022-04-21 12:50:52 +02:00
Jonas Dreßler
c64803770e clutter: Bail out and warn on reentry into mapping/unmapping cycle
There's a bunch of crashes right now where the assertions in
clutter_actor_set_mapped() after calling the map/unmap() vfuncs are
failing. The only way this can happen is by re-entering
clutter_actor_set_mapped() during the map/unmap recursion.

The reason for those crashes is that the shell hides/shows some actors
in response to crossing events and key-focus changes. These in turn get
triggered by the newly introduced ungrabbing of ClutterGrabs when an
actor gets unmapped, which triggers GRAB_NOTIFY crossing events and
key-focus changes.

Since these situations are hardly avoidable (it's a valid use-case to
hide/show something in response to a crossing/key-focus event), catch
the set_mapped() call early while we reenter the mapping machinery and
log a warning instead of crashing.

Fixes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/3165
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2299>
2022-04-21 10:09:59 +00:00
Jonas Dreßler
4a865424cd clutter: Set event as current_event when going through event filters
With the introduction of untrottled event delivery to wayland clients,
we moved the _clutter_event_process_filters() call outside of
_clutter_process_event(). This also moved the processing of event
filters outside of the timespan where the event is added to Clutters
current_event stack, making Clutter.get_current_event() no longer
available to anything happening inside mutters event filter.

One thing that happens in mutters event filter is detecting and
triggering keybindings like the alt-tab switcher. Now the alt-tab
switcher has a special case where it finishes and activates a window
right when the keybinding gets activated, relying on the current event
time as the timestamp to activate the window.

Now since the current event time is no longer available from inside
mutters event filter, we'd pass 0 to meta_window_activate(), causing
mutter to send a notification instead of actually activating the window.

To fix this, also set a current_event for the ClutterContext when going
through event filters, this makes sure Clutter.get_current_event_time()
works when called inside keybinding handlers.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2327>
2022-04-21 09:45:21 +00:00
Carlos Garnacho
91ae72acdf core: Account ClutterStage grabs on Wayland key focus synchronization
When switching between the existence and not of a stage ClutterGrab, we
would correctly attempt to synchronize key focus from the perspective of
the Wayland clients.

But this synchronization should do its own checks about existing stage
grabs before determining a client window has key focus or not.

Add that check, so that grabs correctly unfocus the keyboard in Wayland
clients, in addition to pointers and touch.

Fixes: https://gitlab.gnome.org/GNOME/mutter/-/issues/2194
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2366>
2022-04-20 14:38:23 +00:00
Łukasz Spintzyk
ad838ec540 renderer/native: Restore EGL state after creating secondary GPU state
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2347>
2022-04-20 09:30:04 +00:00
Daniel van Vugt
af7e116b94 cursor-renderer/native: Remove unused DRM_CAP_CURSOR_{WIDTH,HEIGHT}
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2361>
2022-04-20 08:28:06 +00:00
Daniel van Vugt
be9deeba09 crtc/kms: Don't add gamma to the update if unsupported by the CRTC
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/2197
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2360>
2022-04-19 07:58:31 +00:00
Daniel van Vugt
364572b95c kms/crtc: Add function meta_kms_crtc_has_gamma
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2360>
2022-04-19 07:58:31 +00:00
Daniel van Vugt
cc20584032 kms/crtc: Add debug logging of supported vs unsupported properties
Unsupported properties are particularly common in an Nvidia/hybrid
setup so we don't want to make it a warning that's always visible.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2360>
2022-04-19 07:58:31 +00:00
Daniel van Vugt
e3cba9f2d8 kms/crtc: Fix typo s/proporties/properties/
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2360>
2022-04-19 07:58:31 +00:00
Ngọc Quân Trần
6a92997cbe Update Vietnamese translation 2022-04-16 07:08:54 +00:00
Jonas Ådahl
8c656a7f40 startup-notification: Fix some introspection annotations
This avoids a bunch of warnings and fixes a transfer annotation.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2374>
2022-04-13 12:03:27 +02:00
Daniel van Vugt
14ab0238d0 stage/x11: Remove trailing whitespace
To avoid check-code-style warnings in other MRs that touch this file.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2373>
2022-04-13 14:35:59 +08:00
Daniel van Vugt
dd94c448e9 kms/device: Disable modifiers when !DRM_CAP_ADDFB2_MODIFIERS
Fixes: https://gitlab.gnome.org/GNOME/mutter/-/issues/2210
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2359>
2022-04-08 16:20:34 +00:00
Daniel van Vugt
11e6100226 kms/impl-device: Add addfb2_modifiers to MetaKmsDeviceCaps
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2359>
2022-04-08 16:20:34 +00:00
Jordi Mas
6c34c584c8 Update Catalan translation 2022-04-07 22:09:44 +02:00
Jonas Ådahl
9d0f612de1 shaped-texture: Paint with the right layer snippet
When we get passed a "snippet" to the shaped texture, it's added as a
pipeline layer snippet to change how the source texture is sampled. When
we draw from a texture tower however we have allocated regular textures
which doesn't need any special layer snippet, so create separate
pipelines for those that doesn't use that snippet.

Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/528
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2278>
2022-04-07 07:28:44 +00:00
Jonas Ådahl
d4ffaf291f shaped-texture: Pass along the snippet to the texture tower
The snippet is used make sure the right source is sampled in the shader.
This wasn't done in the texture tower, meaning the textures from the
tower were not correct.

Related: https://gitlab.gnome.org/GNOME/mutter/-/issues/528
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2278>
2022-04-07 07:28:44 +00:00
Corentin Noël
c2b8582e0f startup-notification: Specify the actual type in the "changed" signal
Allows to have type checks and the introspection to be aware of the actual type.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2362>
2022-04-05 14:05:23 +02:00
Corentin Noël
9132f984ce mutter: Add missing nullable annotation when possible
Add missing (nullable) annotation when a possible NULL return value is expected.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2362>
2022-04-05 14:05:23 +02:00
Corentin Noël
50c91bca75 cursor-tracker: Add missing annotation to meta_cursor_tracker_get_pointer
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2362>
2022-04-05 14:05:22 +02:00
Corentin Noël
e4c8125c31 cogl/context: Add transfer full annotation to destroy timestamp query
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2362>
2022-04-05 12:40:56 +02:00
Daniel van Vugt
9a23ed98e7 onscreen/native: Fix typo in log message "the the"
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2357>
2022-04-04 17:36:37 +08:00
Carlos Garnacho
f820bb3506 clutter: Keep actors dirty if a redraw was queued up during paint()
In the right combination of circumstances, and given 2 actors (parent
actor P with an offscreen effect and child actor C), we may have the
following situation happening:

- A redraw is queued on the actor C, actors C and P are marked as
  priv->is_dirty and priv->propagated_one_redraw.
- During paint() handling we paint actor P, priv->propagated_one_redraw
  is turned off.
- We recurse into child actor C, priv->propagated_one_redraw is turned
  off.
- A new redraw is queued on actor C, actors C and P are marked as
  priv->is_dirty and priv->propagated_one_redraw.
- The paint() method recurses back, actors C and P get priv->is_dirty
  disabled, priv->propagated_one_redraw remains set.
- At this point queueing up more redraws on actor C will not propagate
  up, because actor C has priv->propagated_one_redraw set, but the
  parent actor P has priv->is_dirty unset, so the offscreen effect will
  not get CLUTTER_EFFECT_PAINT_ACTOR_DIRTY and will avoid repainting
  actor C.

The end result is that actor C does not redraw again, despite requesting
redraws. This situation eventually resolves itself through e.g. relayouts
on actor P, but may take some time to happen.

In order to fix this, consider actors that did get a further redraw
request still dirty after paint().

Fixes: https://gitlab.gnome.org/GNOME/mutter/-/issues/2188
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2353>
2022-03-31 12:39:17 +00:00
Carlos Garnacho
ce7f606d48 clutter: Refactor code marking actors dirty for paint()
Simplify the function arguments (the origin is just the actor that
the function is originally called from), and make it also handle
marking as dirty the actor that got the redraw queued up explicitly.

This makes it a single place where priv->is_dirty is being enabled.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2353>
2022-03-31 12:39:17 +00:00
Jonas Ådahl
47375897a5 wayland/shm: Add support for ABGR8888 and XBGR8888 formats
This avoids shuffling bits for clients drawing in these formats.

Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/2200
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2352>
2022-03-30 14:43:10 +00:00
Olivier Fourdan
eb4307c350 x11: Set WM_S0 last
With Xwayland on demand, a number of maintenance X11 applications need
to be run first, before Xwayland starts accepting requests from the
normal clients, as soon as the WM_S0 selection is acquired by mutter.

On startup, mutter also sets a number of X11 properties that can be
queried by X11 clients.

Unfortunately, mutter acquires the WM_S0 selection before setting those
properties, so mutter and the first regular X11 client will race on
startup.

As a result, the X11 properties set by mutter on startup may not be
available to the very first X11 client when Xwayland starts.

To avoid that issue, make sure to take the WM_S0 selection last when
opening the display.

Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/2176
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2336>
2022-03-30 14:15:03 +00:00
Olivier Fourdan
de8927e075 xwayland: Init DnD on every Xwayland start
Currently, meta_xwayland_shutdown_dnd() is called from the handler
on_x11_display_closing() triggered from the signal "x11-display-closing"
hooked up from meta_xwayland_init_display().

Once the signal has been triggered, on_x11_display_closing() removes the
signal handler, disconnecting from the signal.

As meta_xwayland_init_display() is called from meta_display_new() which
is issued only once, the signal handler is not restored again.

As a result, meta_xwayland_shutdown_dnd() is not called anymore after
Xwayland has been restarted, but meta_xwayland_init_dnd() will check and
assert that the manager's DND object is NULL.

Basically, restarting Xwayland more that once will trigger an assertion
failure in mutter. That's even more of a problem with autoclose-xwayland
where Xwayland is expected to terminate when there is no meaningful X11
client remaining, which can happen multiple times during the lifetime
of a user session.

To make sure that meta_xwayland_init_display() is called for every new
instance of Xwayland, simply keep the signal hooked in place by not
disconnecting it when triggered.

This reverts commit 9a10b8ff94.

Even though, originally, this issue was first introduced with commit
b4fe1fdd95 ("xwayland: Make setup/teardown
a bit more symmetrical") which didn't actually kept 'x11-display-setup'
and 'x11-display-closing' connected.

Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/2168
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2339>
2022-03-30 13:51:26 +00:00
Sebastian Wick
1760dcb0d7 window: Check fullscreen status when window changes monitor
For wayland meta_window_move_to_monitor sends a configure to the client
without actually moving the window, yet and the
meta_display_queue_check_fullscreen call won't detect any changes.
Checking for fullscreen in meta_window_update_monitor fixes the problem
because it is called whenever the window actually changed the monitor it
is on.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2325>
2022-03-30 13:01:29 +00:00
Daniel van Vugt
8354289650 onscreen/native: Fall back if COPY_MODE_SECONDARY_GPU fails to init
This can happen if we've got a GBM driver that is incomplete or
buggy for any reason. Currently this includes the Nvidia driver
when used as a secondary GPU, and as soon as they fix that it will
use the fast path instead.

Falling back means we can still use it as a KMS device with all rendering
done on the primary GPU instead. Just like a DisplayLink.

It's slow, but at least the screens all light up now and you get a
usable desktop. Surprisingly it's still a better experience than using
Xorg, maybe because we can flip the outputs independently with the
native backend.

Fixes: https://gitlab.gnome.org/GNOME/mutter/-/issues/2166,
       https://gitlab.gnome.org/GNOME/mutter/-/issues/2182,
       https://launchpad.net/bugs/1959888,
       https://launchpad.net/bugs/1964037

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2341>
2022-03-30 12:36:05 +00:00
Daniel van Vugt
41bfabad96 onscreen/native: Don't try rendering on a secondary GPU without EGL
We rather confusingly still call a secondary display card that is
GPU-less (DisplayLink or other basic KMS device) a "secondary GPU",
so just because secondary_gpu_state is non-NULL doesn't mean we
can use it for rendering. The clearest indication of this is when
there is no EGL surface.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2341>
2022-03-30 12:36:05 +00:00
Carlos Garnacho
861c4b8535 core: Port to input device capabilities in pad mapper
Check for tablets and pad devices using capabilities.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2331>
2022-03-30 12:06:52 +00:00
Carlos Garnacho
286562ea53 backends: Update MetaInputSettings to apply settings based on capabilities
Since devices may be multiple things now, check all capabilities in order
to ensure all aspects of the device are correctly configured.

This change does the following observations:
- Devices that have TOUCHPAD | POINTER capabilities prefer the 'touchpad'
  settings path. The regular pointer settings path is left for all
  non-touchpads.
- Devices that are both a tablet and a touchscreen prefer the tablet
  relocatable schema. This works for both aspects as the touchscreen
  schema is a subset of the tablet one.

Other than that it's a rather boring, even if verbose search and replace.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2331>
2022-03-30 12:06:52 +00:00
Carlos Garnacho
fff3654941 wayland: Check input device capabilities in tablet seats
Instead of looking for tablets and pads based on input device type,
check capabilities.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2331>
2022-03-30 12:06:52 +00:00
Carlos Garnacho
844a729fa9 wayland: Set wayland seat capabilities based on input device capabilities
Instead of looking at device types, which might be incomplete.

Fixes: https://gitlab.gnome.org/GNOME/mutter/-/issues/2154
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2331>
2022-03-30 12:06:52 +00:00
Carlos Garnacho
1fda60f03e clutter: Add compatibility code to get input capabilities from device type
We not just have X11 devices, but also virtual devices on both backends.
In the mean time, keep these working on top of a ClutterInputDeviceType,
but transform that into capabilities on device construction so users can
rely on the new flagset.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2331>
2022-03-30 12:06:52 +00:00
Carlos Garnacho
1f3fb8441f backends/native: Assign capabilities to input devices
These are extracted from the individual libinput_devices.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2331>
2022-03-30 12:06:52 +00:00
Carlos Garnacho
15d4402d08 clutter: Add ClutterInputDevice::capabilities property
This is construct-only, and assigned by the backend.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2331>
2022-03-30 12:06:52 +00:00