2022-03-21 15:17:04 +01:00
# include "../Compositor.hpp"
# include "../helpers/WLClasses.hpp"
2022-06-09 12:46:55 +02:00
# include "../managers/input/InputManager.hpp"
2022-03-21 15:17:04 +01:00
# include "../render/Renderer.hpp"
# include "Events.hpp"
// --------------------------------------------- //
// _ __ ________ _____ _____ //
// | | /\\ \ / / ____| __ \ / ____| //
// | | / \\ \_/ /| |__ | |__) | (___ //
// | | / /\ \\ / | __| | _ / \___ \ //
// | |____ / ____ \| | | |____| | \ \ ____) | //
// |______/_/ \_\_| |______|_| \_\_____/ //
// //
// --------------------------------------------- //
void Events : : listener_newLayerSurface ( wl_listener * listener , void * data ) {
const auto WLRLAYERSURFACE = ( wlr_layer_surface_v1 * ) data ;
if ( ! WLRLAYERSURFACE - > output ) {
const auto PMONITOR = g_pCompositor - > getMonitorFromCursor ( ) ;
if ( ! PMONITOR ) {
Debug : : log ( ERR , " No monitor at cursor on new layer without a monitor. Ignoring. " ) ;
wlr_layer_surface_v1_destroy ( WLRLAYERSURFACE ) ;
return ;
}
2022-06-09 17:32:58 +02:00
Debug : : log ( LOG , " New LayerSurface has no preferred monitor. Assigning Monitor %s " , PMONITOR - > szName . c_str ( ) ) ;
2022-03-21 15:17:04 +01:00
WLRLAYERSURFACE - > output = PMONITOR - > output ;
}
2022-11-03 09:56:41 +01:00
auto PMONITOR = ( CMonitor * ) g_pCompositor - > getMonitorFromOutput ( WLRLAYERSURFACE - > output ) ;
if ( ! WLRLAYERSURFACE - > output | | ! PMONITOR | | PMONITOR - > pMirrorOf ) {
2022-12-16 18:17:31 +01:00
PMONITOR = g_pCompositor - > m_vMonitors . front ( ) . get ( ) ;
2022-11-03 09:56:41 +01:00
WLRLAYERSURFACE - > output = PMONITOR - > output ; // TODO: current mon
}
2023-01-22 17:03:25 +01:00
SLayerSurface * layerSurface = PMONITOR - > m_aLayerSurfaceLayers [ WLRLAYERSURFACE - > pending . layer ] . emplace_back ( std : : make_unique < SLayerSurface > ( ) ) . get ( ) ;
2022-07-06 21:57:35 +02:00
layerSurface - > szNamespace = WLRLAYERSURFACE - > _namespace ;
2022-03-21 15:17:04 +01:00
2022-03-28 22:31:39 +02:00
layerSurface - > hyprListener_commitLayerSurface . initCallback ( & WLRLAYERSURFACE - > surface - > events . commit , & Events : : listener_commitLayerSurface , layerSurface , " layerSurface " ) ;
2022-07-26 21:59:07 +02:00
layerSurface - > hyprListener_destroyLayerSurface . initCallback ( & WLRLAYERSURFACE - > events . destroy , & Events : : listener_destroyLayerSurface , layerSurface , " layerSurface " ) ;
2022-03-28 22:31:39 +02:00
layerSurface - > hyprListener_mapLayerSurface . initCallback ( & WLRLAYERSURFACE - > events . map , & Events : : listener_mapLayerSurface , layerSurface , " layerSurface " ) ;
layerSurface - > hyprListener_unmapLayerSurface . initCallback ( & WLRLAYERSURFACE - > events . unmap , & Events : : listener_unmapLayerSurface , layerSurface , " layerSurface " ) ;
2022-07-22 13:34:19 +02:00
layerSurface - > hyprListener_newPopup . initCallback ( & WLRLAYERSURFACE - > events . new_popup , & Events : : listener_newPopup , layerSurface , " layerSurface " ) ;
2022-03-21 15:17:04 +01:00
layerSurface - > layerSurface = WLRLAYERSURFACE ;
2022-12-16 18:17:31 +01:00
layerSurface - > layer = WLRLAYERSURFACE - > current . layer ;
WLRLAYERSURFACE - > data = layerSurface ;
layerSurface - > monitorID = PMONITOR - > ID ;
2022-03-21 15:17:04 +01:00
2022-07-06 22:12:03 +02:00
layerSurface - > forceBlur = g_pConfigManager - > shouldBlurLS ( layerSurface - > szNamespace ) ;
2022-12-16 18:17:31 +01:00
Debug : : log ( LOG , " LayerSurface %x (namespace %s layer %d) created on monitor %s " , layerSurface - > layerSurface , layerSurface - > layerSurface - > _namespace , layerSurface - > layer ,
PMONITOR - > szName . c_str ( ) ) ;
2022-03-21 15:17:04 +01:00
}
2022-03-28 22:31:39 +02:00
void Events : : listener_destroyLayerSurface ( void * owner , void * data ) {
SLayerSurface * layersurface = ( SLayerSurface * ) owner ;
2022-03-21 15:17:04 +01:00
2022-03-24 21:34:24 +01:00
Debug : : log ( LOG , " LayerSurface %x destroyed " , layersurface - > layerSurface ) ;
2022-05-30 17:11:35 +02:00
const auto PMONITOR = g_pCompositor - > getMonitorFromID ( layersurface - > monitorID ) ;
if ( ! g_pCompositor - > getMonitorFromID ( layersurface - > monitorID ) )
Debug : : log ( WARN , " Layersurface destroyed on an invalid monitor (removed?) " ) ;
2022-07-25 20:47:56 +02:00
if ( ! layersurface - > fadingOut ) {
2022-07-25 18:38:40 +02:00
if ( layersurface - > mapped ) {
Debug : : log ( LOG , " Forcing an unmap of a LS that did a straight destroy! " ) ;
listener_unmapLayerSurface ( layersurface , nullptr ) ;
} else {
Debug : : log ( LOG , " Removing LayerSurface that wasn't mapped. " ) ;
layersurface - > alpha . setValueAndWarp ( 0.f ) ;
layersurface - > fadingOut = true ;
2022-07-25 21:08:54 +02:00
g_pCompositor - > addToFadingOutSafe ( layersurface ) ;
2022-07-25 18:38:40 +02:00
}
2022-05-15 11:25:42 +02:00
}
2022-07-18 21:16:01 +02:00
layersurface - > noProcess = true ;
2022-07-26 22:01:55 +02:00
layersurface - > hyprListener_commitLayerSurface . removeCallback ( ) ;
2022-03-28 22:31:39 +02:00
layersurface - > hyprListener_destroyLayerSurface . removeCallback ( ) ;
layersurface - > hyprListener_mapLayerSurface . removeCallback ( ) ;
layersurface - > hyprListener_unmapLayerSurface . removeCallback ( ) ;
2022-07-22 13:34:19 +02:00
layersurface - > hyprListener_newPopup . removeCallback ( ) ;
2022-03-21 15:17:04 +01:00
2022-03-21 16:33:38 +01:00
// rearrange to fix the reserved areas
if ( PMONITOR ) {
g_pHyprRenderer - > arrangeLayersForMonitor ( PMONITOR - > ID ) ;
2022-07-26 18:22:34 +02:00
PMONITOR - > scheduledRecalc = true ;
2022-04-24 12:04:16 +02:00
// and damage
2022-12-16 18:17:31 +01:00
wlr_box geomFixed = { layersurface - > geometry . x + PMONITOR - > vecPosition . x , layersurface - > geometry . y + PMONITOR - > vecPosition . y , layersurface - > geometry . width ,
layersurface - > geometry . height } ;
2022-04-24 12:04:16 +02:00
g_pHyprRenderer - > damageBox ( & geomFixed ) ;
2022-03-21 16:33:38 +01:00
}
2022-05-14 11:10:50 +02:00
2022-05-14 17:23:46 +02:00
layersurface - > readyToDelete = true ;
2022-12-16 18:17:31 +01:00
layersurface - > layerSurface = nullptr ;
2022-03-21 15:17:04 +01:00
}
2022-03-28 22:31:39 +02:00
void Events : : listener_mapLayerSurface ( void * owner , void * data ) {
SLayerSurface * layersurface = ( SLayerSurface * ) owner ;
2022-03-21 15:17:04 +01:00
2022-03-24 21:34:24 +01:00
Debug : : log ( LOG , " LayerSurface %x mapped " , layersurface - > layerSurface ) ;
2022-03-21 16:33:38 +01:00
layersurface - > layerSurface - > mapped = true ;
2022-12-16 18:17:31 +01:00
layersurface - > mapped = true ;
2022-03-21 16:33:38 +01:00
2022-07-28 13:28:43 +02:00
// anim
layersurface - > alpha . setConfig ( g_pConfigManager - > getAnimationPropertyConfig ( " fadeIn " ) ) ;
2022-03-21 15:17:04 +01:00
// fix if it changed its mon
const auto PMONITOR = g_pCompositor - > getMonitorFromOutput ( layersurface - > layerSurface - > output ) ;
2022-04-24 12:04:16 +02:00
if ( ! PMONITOR )
return ;
2023-01-25 16:34:13 +01:00
for ( auto & rule : g_pConfigManager - > getMatchingRules ( layersurface ) ) {
if ( rule . rule = = " noanim " )
layersurface - > noAnimations = true ;
}
2022-03-21 15:17:04 +01:00
if ( ( uint64_t ) layersurface - > monitorID ! = PMONITOR - > ID ) {
const auto POLDMON = g_pCompositor - > getMonitorFromID ( layersurface - > monitorID ) ;
2023-01-22 17:03:25 +01:00
for ( auto it = POLDMON - > m_aLayerSurfaceLayers [ layersurface - > layer ] . begin ( ) ; it ! = POLDMON - > m_aLayerSurfaceLayers [ layersurface - > layer ] . end ( ) ; it + + ) {
2022-07-23 15:48:08 +02:00
if ( it - > get ( ) = = layersurface ) {
2023-01-22 17:03:25 +01:00
PMONITOR - > m_aLayerSurfaceLayers [ layersurface - > layer ] . emplace_back ( std : : move ( * it ) ) ;
POLDMON - > m_aLayerSurfaceLayers [ layersurface - > layer ] . erase ( it ) ;
2022-07-23 15:48:08 +02:00
break ;
}
}
2022-12-16 18:17:31 +01:00
layersurface - > monitorID = PMONITOR - > ID ;
2022-07-26 18:22:34 +02:00
PMONITOR - > scheduledRecalc = true ;
2022-03-21 15:17:04 +01:00
g_pHyprRenderer - > arrangeLayersForMonitor ( POLDMON - > ID ) ;
}
g_pHyprRenderer - > arrangeLayersForMonitor ( PMONITOR - > ID ) ;
2022-09-29 23:16:37 +02:00
wlr_surface_send_enter ( layersurface - > layerSurface - > surface , layersurface - > layerSurface - > output ) ;
2022-12-16 18:17:31 +01:00
if ( layersurface - > layerSurface - > current . keyboard_interactive & &
( ! g_pCompositor - > m_sSeat . mouse | | ! g_pCompositor - > m_sSeat . mouse - > currentConstraint ) ) { // don't focus if constrained
2022-03-21 15:17:04 +01:00
g_pCompositor - > focusSurface ( layersurface - > layerSurface - > surface ) ;
2022-03-27 17:25:20 +02:00
2022-12-16 18:17:31 +01:00
const auto LOCAL =
g_pInputManager - > getMouseCoordsInternal ( ) - Vector2D ( layersurface - > geometry . x + PMONITOR - > vecPosition . x , layersurface - > geometry . y + PMONITOR - > vecPosition . y ) ;
2022-07-14 21:33:36 +02:00
wlr_seat_pointer_notify_enter ( g_pCompositor - > m_sSeat . seat , layersurface - > layerSurface - > surface , LOCAL . x , LOCAL . y ) ;
wlr_seat_pointer_notify_motion ( g_pCompositor - > m_sSeat . seat , 0 , LOCAL . x , LOCAL . y ) ;
}
2022-07-08 13:19:57 +02:00
2022-03-27 17:25:20 +02:00
layersurface - > position = Vector2D ( layersurface - > geometry . x , layersurface - > geometry . y ) ;
2022-04-24 12:04:16 +02:00
2022-12-16 18:17:31 +01:00
wlr_box geomFixed = { layersurface - > geometry . x + PMONITOR - > vecPosition . x , layersurface - > geometry . y + PMONITOR - > vecPosition . y , layersurface - > geometry . width ,
layersurface - > geometry . height } ;
2022-04-24 12:04:16 +02:00
g_pHyprRenderer - > damageBox ( & geomFixed ) ;
2022-05-14 17:23:46 +02:00
layersurface - > alpha . setValue ( 0 ) ;
2023-01-05 19:25:45 +01:00
layersurface - > alpha = 1.f ;
2022-05-14 17:23:46 +02:00
layersurface - > readyToDelete = false ;
2022-12-16 18:17:31 +01:00
layersurface - > fadingOut = false ;
2022-10-07 23:23:50 +02:00
2023-01-25 16:34:13 +01:00
if ( layersurface - > noAnimations )
layersurface - > alpha . setValueAndWarp ( 1.f ) ;
2022-10-07 23:23:50 +02:00
g_pEventManager - > postEvent ( SHyprIPCEvent { " openlayer " , std : : string ( layersurface - > layerSurface - > _namespace ? layersurface - > layerSurface - > _namespace : " " ) } ) ;
2022-03-21 15:17:04 +01:00
}
2022-03-28 22:31:39 +02:00
void Events : : listener_unmapLayerSurface ( void * owner , void * data ) {
SLayerSurface * layersurface = ( SLayerSurface * ) owner ;
2022-03-21 15:17:04 +01:00
2022-03-24 21:34:24 +01:00
Debug : : log ( LOG , " LayerSurface %x unmapped " , layersurface - > layerSurface ) ;
2022-10-07 23:23:50 +02:00
g_pEventManager - > postEvent ( SHyprIPCEvent { " closelayer " , std : : string ( layersurface - > layerSurface - > _namespace ? layersurface - > layerSurface - > _namespace : " " ) } ) ;
2022-09-05 22:35:41 +02:00
if ( ! g_pCompositor - > getMonitorFromID ( layersurface - > monitorID ) | | g_pCompositor - > m_bUnsafeState ) {
2022-05-30 17:11:35 +02:00
Debug : : log ( WARN , " Layersurface unmapping on invalid monitor (removed?) ignoring. " ) ;
2022-07-25 20:47:56 +02:00
2022-07-25 21:08:54 +02:00
g_pCompositor - > addToFadingOutSafe ( layersurface ) ;
2022-07-25 20:47:56 +02:00
layersurface - > mapped = false ;
layersurface - > fadingOut = true ;
layersurface - > alpha . setValueAndWarp ( 0.f ) ;
2022-05-30 17:11:35 +02:00
return ;
}
2022-07-28 13:28:43 +02:00
// anim
layersurface - > alpha . setConfig ( g_pConfigManager - > getAnimationPropertyConfig ( " fadeOut " ) ) ;
2022-05-14 17:23:46 +02:00
// make a snapshot and start fade
g_pHyprOpenGL - > makeLayerSnapshot ( layersurface ) ;
layersurface - > alpha = 0.f ;
2023-01-25 16:34:13 +01:00
if ( layersurface - > noAnimations )
layersurface - > alpha . setValueAndWarp ( 0.f ) ;
2022-07-25 18:38:40 +02:00
layersurface - > mapped = false ;
2022-05-14 17:23:46 +02:00
layersurface - > fadingOut = true ;
2022-07-25 21:08:54 +02:00
g_pCompositor - > addToFadingOutSafe ( layersurface ) ;
2022-05-14 17:23:46 +02:00
2022-03-21 15:17:04 +01:00
if ( layersurface - > layerSurface - > mapped )
2022-03-21 16:33:38 +01:00
layersurface - > layerSurface - > mapped = false ;
2022-03-21 15:17:04 +01:00
2022-04-24 12:04:16 +02:00
const auto PMONITOR = g_pCompositor - > getMonitorFromOutput ( layersurface - > layerSurface - > output ) ;
if ( ! PMONITOR )
return ;
2022-07-08 13:19:57 +02:00
// refocus if needed
if ( layersurface - > layerSurface - > surface = = g_pCompositor - > m_pLastFocus ) {
2022-12-16 18:17:31 +01:00
Vector2D surfaceCoords ;
2022-10-25 15:19:24 +02:00
SLayerSurface * pFoundLayerSurface = nullptr ;
2022-12-16 18:17:31 +01:00
wlr_surface * foundSurface = nullptr ;
2022-10-25 15:19:24 +02:00
2022-07-08 13:19:57 +02:00
g_pCompositor - > m_pLastFocus = nullptr ;
2022-10-25 15:19:24 +02:00
// find LS-es to focus
2023-01-22 17:03:25 +01:00
foundSurface = g_pCompositor - > vectorToLayerSurface ( g_pInputManager - > getMouseCoordsInternal ( ) , & PMONITOR - > m_aLayerSurfaceLayers [ ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY ] ,
2022-12-16 18:17:31 +01:00
& surfaceCoords , & pFoundLayerSurface ) ;
2022-10-25 15:19:24 +02:00
if ( ! foundSurface )
2023-01-22 17:03:25 +01:00
foundSurface = g_pCompositor - > vectorToLayerSurface ( g_pInputManager - > getMouseCoordsInternal ( ) , & PMONITOR - > m_aLayerSurfaceLayers [ ZWLR_LAYER_SHELL_V1_LAYER_TOP ] ,
2022-12-16 18:17:31 +01:00
& surfaceCoords , & pFoundLayerSurface ) ;
2022-10-25 15:19:24 +02:00
if ( ! foundSurface ) {
// if there isn't any, focus the last window
const auto PLASTWINDOW = g_pCompositor - > m_pLastWindow ;
g_pCompositor - > focusWindow ( nullptr ) ;
g_pCompositor - > focusWindow ( PLASTWINDOW ) ;
} else {
// otherwise, full refocus
g_pInputManager - > refocus ( ) ;
}
2022-07-08 13:19:57 +02:00
}
2022-12-16 18:17:31 +01:00
wlr_box geomFixed = { layersurface - > geometry . x + PMONITOR - > vecPosition . x , layersurface - > geometry . y + PMONITOR - > vecPosition . y , layersurface - > geometry . width ,
layersurface - > geometry . height } ;
2022-04-24 12:04:16 +02:00
g_pHyprRenderer - > damageBox ( & geomFixed ) ;
2022-06-01 13:38:33 +02:00
2022-12-16 18:17:31 +01:00
geomFixed = { layersurface - > geometry . x + ( int ) PMONITOR - > vecPosition . x , layersurface - > geometry . y + ( int ) PMONITOR - > vecPosition . y ,
( int ) layersurface - > layerSurface - > surface - > current . width , ( int ) layersurface - > layerSurface - > surface - > current . height } ;
2022-06-01 13:38:33 +02:00
g_pHyprRenderer - > damageBox ( & geomFixed ) ;
2022-12-16 18:17:31 +01:00
geomFixed = { layersurface - > geometry . x , layersurface - > geometry . y , ( int ) layersurface - > layerSurface - > surface - > current . width ,
2023-01-25 16:34:13 +01:00
( int ) layersurface - > layerSurface - > surface - > current . height } ;
2022-06-01 13:38:33 +02:00
layersurface - > geometry = geomFixed ; // because the surface can overflow... for some reason?
2022-03-21 15:17:04 +01:00
}
2022-03-28 22:31:39 +02:00
void Events : : listener_commitLayerSurface ( void * owner , void * data ) {
SLayerSurface * layersurface = ( SLayerSurface * ) owner ;
2022-03-21 15:17:04 +01:00
if ( ! layersurface - > layerSurface | | ! layersurface - > layerSurface - > output )
return ;
const auto PMONITOR = g_pCompositor - > getMonitorFromOutput ( layersurface - > layerSurface - > output ) ;
if ( ! PMONITOR )
return ;
2022-08-01 12:16:33 +02:00
if ( layersurface - > layer = = ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND | | layersurface - > layer = = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM )
2022-12-16 18:17:31 +01:00
g_pHyprOpenGL - > markBlurDirtyForMonitor ( PMONITOR ) ; // so that blur is recalc'd
2022-08-01 12:16:33 +02:00
2022-07-01 22:49:12 +02:00
wlr_box geomFixed = { layersurface - > geometry . x , layersurface - > geometry . y , layersurface - > geometry . width , layersurface - > geometry . height } ;
2022-04-24 12:04:16 +02:00
g_pHyprRenderer - > damageBox ( & geomFixed ) ;
2022-03-21 15:17:04 +01:00
// fix if it changed its mon
if ( ( uint64_t ) layersurface - > monitorID ! = PMONITOR - > ID ) {
const auto POLDMON = g_pCompositor - > getMonitorFromID ( layersurface - > monitorID ) ;
2022-07-23 15:48:08 +02:00
2023-01-22 17:03:25 +01:00
for ( auto it = POLDMON - > m_aLayerSurfaceLayers [ layersurface - > layer ] . begin ( ) ; it ! = POLDMON - > m_aLayerSurfaceLayers [ layersurface - > layer ] . end ( ) ; it + + ) {
2022-07-23 15:48:08 +02:00
if ( it - > get ( ) = = layersurface ) {
2023-01-22 17:03:25 +01:00
PMONITOR - > m_aLayerSurfaceLayers [ layersurface - > layer ] . emplace_back ( std : : move ( * it ) ) ;
POLDMON - > m_aLayerSurfaceLayers [ layersurface - > layer ] . erase ( it ) ;
2022-07-23 15:48:08 +02:00
break ;
}
}
2022-12-16 18:17:31 +01:00
layersurface - > monitorID = PMONITOR - > ID ;
2022-07-26 18:22:34 +02:00
PMONITOR - > scheduledRecalc = true ;
2022-03-21 15:17:04 +01:00
g_pHyprRenderer - > arrangeLayersForMonitor ( POLDMON - > ID ) ;
}
2022-04-17 10:46:04 +02:00
if ( layersurface - > layerSurface - > current . committed ! = 0 ) {
if ( layersurface - > layer ! = layersurface - > layerSurface - > current . layer ) {
2022-09-25 20:07:48 +02:00
2023-01-22 17:03:25 +01:00
for ( auto it = PMONITOR - > m_aLayerSurfaceLayers [ layersurface - > layer ] . begin ( ) ; it ! = PMONITOR - > m_aLayerSurfaceLayers [ layersurface - > layer ] . end ( ) ; it + + ) {
2022-07-23 15:48:08 +02:00
if ( it - > get ( ) = = layersurface ) {
2023-01-22 17:03:25 +01:00
PMONITOR - > m_aLayerSurfaceLayers [ layersurface - > layerSurface - > current . layer ] . emplace_back ( std : : move ( * it ) ) ;
PMONITOR - > m_aLayerSurfaceLayers [ layersurface - > layer ] . erase ( it ) ;
2022-07-23 15:48:08 +02:00
break ;
}
}
2022-04-17 10:46:04 +02:00
layersurface - > layer = layersurface - > layerSurface - > current . layer ;
2022-08-01 12:16:33 +02:00
if ( layersurface - > layer = = ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND | | layersurface - > layer = = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM )
2022-12-16 18:17:31 +01:00
g_pHyprOpenGL - > markBlurDirtyForMonitor ( PMONITOR ) ; // so that blur is recalc'd
2022-04-17 10:46:04 +02:00
}
2022-03-21 15:17:04 +01:00
2022-07-25 23:53:43 +02:00
g_pHyprRenderer - > arrangeLayersForMonitor ( PMONITOR - > ID ) ;
2022-07-26 18:22:34 +02:00
PMONITOR - > scheduledRecalc = true ;
2022-04-17 10:46:04 +02:00
}
2022-03-21 15:17:04 +01:00
2022-03-27 17:25:20 +02:00
layersurface - > position = Vector2D ( layersurface - > geometry . x , layersurface - > geometry . y ) ;
2022-04-14 20:22:14 +02:00
2022-07-10 20:36:32 +02:00
// update geom if it changed
2022-12-16 18:17:31 +01:00
layersurface - > geometry = { layersurface - > geometry . x , layersurface - > geometry . y , layersurface - > layerSurface - > surface - > current . width ,
layersurface - > layerSurface - > surface - > current . height } ;
2022-07-10 20:36:32 +02:00
2022-06-30 18:46:00 +02:00
g_pHyprRenderer - > damageSurface ( layersurface - > layerSurface - > surface , layersurface - > position . x , layersurface - > position . y ) ;
2022-09-25 20:07:48 +02:00
}