Compare commits
	
		
			2 commits
		
	
	
		
			4eac0b40c3
			...
			0b529c6682
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 0b529c6682 | |||
| cbdfba1a3f | 
					 14 changed files with 43 additions and 54 deletions
				
			
		| 
						 | 
				
			
			@ -1,17 +1,21 @@
 | 
			
		|||
#include "session_lock.hpp"
 | 
			
		||||
 | 
			
		||||
#include <qcolor.h>
 | 
			
		||||
#include <qcoreapplication.h>
 | 
			
		||||
#include <qguiapplication.h>
 | 
			
		||||
#include <qlogging.h>
 | 
			
		||||
#include <qobject.h>
 | 
			
		||||
#include <qqmlcomponent.h>
 | 
			
		||||
#include <qqmlengine.h>
 | 
			
		||||
#include <qqmllist.h>
 | 
			
		||||
#include <qquickitem.h>
 | 
			
		||||
#include <qquickwindow.h>
 | 
			
		||||
#include <qscreen.h>
 | 
			
		||||
#include <qtmetamacros.h>
 | 
			
		||||
#include <qtypes.h>
 | 
			
		||||
 | 
			
		||||
#include "../core/qmlscreen.hpp"
 | 
			
		||||
#include "../core/reload.hpp"
 | 
			
		||||
#include "session_lock/session_lock.hpp"
 | 
			
		||||
 | 
			
		||||
void SessionLock::onReload(QObject* oldInstance) {
 | 
			
		||||
| 
						 | 
				
			
			@ -85,7 +89,10 @@ void SessionLock::updateSurfaces(SessionLock* old) {
 | 
			
		|||
				instance->onReload(oldInstance);
 | 
			
		||||
 | 
			
		||||
				this->surfaces[screen] = instance;
 | 
			
		||||
				instance->show();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			for (auto* surface: this->surfaces.values()) {
 | 
			
		||||
				surface->show();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -113,7 +120,7 @@ bool SessionLock::isLocked() const {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
bool SessionLock::isSecure() const {
 | 
			
		||||
	return this->manager != nullptr && SessionLockManager::sessionLocked();
 | 
			
		||||
	return this->manager != nullptr && SessionLockManager::isSecure();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SessionLock::setLocked(bool locked) {
 | 
			
		||||
| 
						 | 
				
			
			@ -136,12 +143,6 @@ void SessionLock::setLocked(bool locked) {
 | 
			
		|||
 | 
			
		||||
QQmlComponent* SessionLock::surfaceComponent() const { return this->mSurfaceComponent; }
 | 
			
		||||
 | 
			
		||||
void SessionLock::rip() {
 | 
			
		||||
	if (this->isLocked()) {
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SessionLock::setSurfaceComponent(QQmlComponent* surfaceComponent) {
 | 
			
		||||
	if (this->mSurfaceComponent != nullptr) this->mSurfaceComponent->deleteLater();
 | 
			
		||||
	if (surfaceComponent != nullptr) surfaceComponent->setParent(this);
 | 
			
		||||
| 
						 | 
				
			
			@ -194,7 +195,7 @@ void SessionLockSurface::onReload(QObject* oldInstance) {
 | 
			
		|||
	// clang-format on
 | 
			
		||||
 | 
			
		||||
	if (auto* parent = qobject_cast<SessionLock*>(this->parent())) {
 | 
			
		||||
		if (!this->ext->attach(window, parent->manager)) {
 | 
			
		||||
		if (!this->ext->attach(this->window, parent->manager)) {
 | 
			
		||||
			qWarning(
 | 
			
		||||
			) << "Failed to attach LockWindowExtension to window. Surface will not behave correctly.";
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -202,9 +203,6 @@ void SessionLockSurface::onReload(QObject* oldInstance) {
 | 
			
		|||
		qWarning(
 | 
			
		||||
		) << "SessionLockSurface parent is not a SessionLock. Surface will not behave correctly.";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// without this the dangling screen pointer wont be updated to a real screen
 | 
			
		||||
	emit this->screenChanged();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QQuickWindow* SessionLockSurface::disownWindow() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,7 +15,6 @@
 | 
			
		|||
#include <qtmetamacros.h>
 | 
			
		||||
#include <qtypes.h>
 | 
			
		||||
 | 
			
		||||
#include "../core/doc.hpp"
 | 
			
		||||
#include "../core/qmlscreen.hpp"
 | 
			
		||||
#include "../core/reload.hpp"
 | 
			
		||||
#include "session_lock/session_lock.hpp"
 | 
			
		||||
| 
						 | 
				
			
			@ -49,8 +48,6 @@ public:
 | 
			
		|||
	[[nodiscard]] QQmlComponent* surfaceComponent() const;
 | 
			
		||||
	void setSurfaceComponent(QQmlComponent* surfaceComponent);
 | 
			
		||||
 | 
			
		||||
	QSDOC_HIDE Q_INVOKABLE void rip();
 | 
			
		||||
 | 
			
		||||
signals:
 | 
			
		||||
	void lockStateChanged();
 | 
			
		||||
	void secureStateChanged();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
#include "lock.hpp"
 | 
			
		||||
 | 
			
		||||
#include <private/qwaylandshellintegration_p.h>
 | 
			
		||||
#include <qtmetamacros.h>
 | 
			
		||||
#include <wayland-ext-session-lock-v1-client-protocol.h>
 | 
			
		||||
 | 
			
		||||
#include "manager.hpp"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,5 @@
 | 
			
		|||
#include "manager.hpp"
 | 
			
		||||
 | 
			
		||||
#include <qdebug.h>
 | 
			
		||||
#include <qlogging.h>
 | 
			
		||||
#include <qwaylandclientextension.h>
 | 
			
		||||
 | 
			
		||||
#include "lock.hpp"
 | 
			
		||||
| 
						 | 
				
			
			@ -20,3 +18,6 @@ QSWaylandSessionLock* QSWaylandSessionLockManager::acquireLock() {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
bool QSWaylandSessionLockManager::isLocked() const { return this->active != nullptr; }
 | 
			
		||||
bool QSWaylandSessionLockManager::isSecure() const {
 | 
			
		||||
	return this->isLocked() && this->active->hasCompositorLock();
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,6 +17,7 @@ public:
 | 
			
		|||
	// Create a new session lock if there is no currently active lock, otherwise null.
 | 
			
		||||
	QSWaylandSessionLock* acquireLock();
 | 
			
		||||
	[[nodiscard]] bool isLocked() const;
 | 
			
		||||
	[[nodiscard]] bool isSecure() const;
 | 
			
		||||
 | 
			
		||||
	static bool sessionLocked();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,8 +10,9 @@
 | 
			
		|||
#include "shell_integration.hpp"
 | 
			
		||||
#include "surface.hpp"
 | 
			
		||||
 | 
			
		||||
static QSWaylandSessionLockManager* manager() {
 | 
			
		||||
	static QSWaylandSessionLockManager* manager = nullptr;
 | 
			
		||||
namespace {
 | 
			
		||||
QSWaylandSessionLockManager* manager() {
 | 
			
		||||
	static QSWaylandSessionLockManager* manager = nullptr; // NOLINT
 | 
			
		||||
 | 
			
		||||
	if (manager == nullptr) {
 | 
			
		||||
		manager = new QSWaylandSessionLockManager();
 | 
			
		||||
| 
						 | 
				
			
			@ -19,6 +20,7 @@ static QSWaylandSessionLockManager* manager() {
 | 
			
		|||
 | 
			
		||||
	return manager;
 | 
			
		||||
}
 | 
			
		||||
} // namespace
 | 
			
		||||
 | 
			
		||||
bool SessionLockManager::lock() {
 | 
			
		||||
	if (this->isLocked() || SessionLockManager::sessionLocked()) return false;
 | 
			
		||||
| 
						 | 
				
			
			@ -42,6 +44,7 @@ bool SessionLockManager::unlock() {
 | 
			
		|||
 | 
			
		||||
bool SessionLockManager::isLocked() const { return this->mLock != nullptr; }
 | 
			
		||||
bool SessionLockManager::sessionLocked() { return manager()->isLocked(); }
 | 
			
		||||
bool SessionLockManager::isSecure() { return manager()->isSecure(); }
 | 
			
		||||
 | 
			
		||||
LockWindowExtension::~LockWindowExtension() {
 | 
			
		||||
	if (this->surface != nullptr) {
 | 
			
		||||
| 
						 | 
				
			
			@ -79,7 +82,7 @@ bool LockWindowExtension::attach(QWindow* window, SessionLockManager* manager) {
 | 
			
		|||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		static QSWaylandSessionLockIntegration* lockIntegration = nullptr;
 | 
			
		||||
		static QSWaylandSessionLockIntegration* lockIntegration = nullptr; // NOLINT
 | 
			
		||||
		if (lockIntegration == nullptr) {
 | 
			
		||||
			lockIntegration = new QSWaylandSessionLockIntegration();
 | 
			
		||||
			if (!lockIntegration->initialize(waylandWindow->display())) {
 | 
			
		||||
| 
						 | 
				
			
			@ -105,6 +108,6 @@ bool LockWindowExtension::attach(QWindow* window, SessionLockManager* manager) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void LockWindowExtension::setVisible() {
 | 
			
		||||
	if (this->surface == nullptr) immediatelyVisible = true;
 | 
			
		||||
	if (this->surface == nullptr) this->immediatelyVisible = true;
 | 
			
		||||
	else this->surface->setVisible();
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,7 +14,6 @@ class SessionLockManager: public QObject {
 | 
			
		|||
 | 
			
		||||
public:
 | 
			
		||||
	explicit SessionLockManager(QObject* parent = nullptr): QObject(parent) {}
 | 
			
		||||
	Q_DISABLE_COPY_MOVE(SessionLockManager);
 | 
			
		||||
 | 
			
		||||
	// Returns true if a lock was acquired.
 | 
			
		||||
	// If true is returned the caller must watch the global screen list and create/destroy
 | 
			
		||||
| 
						 | 
				
			
			@ -27,6 +26,7 @@ public:
 | 
			
		|||
	[[nodiscard]] bool isLocked() const;
 | 
			
		||||
 | 
			
		||||
	static bool sessionLocked();
 | 
			
		||||
	static bool isSecure();
 | 
			
		||||
 | 
			
		||||
signals:
 | 
			
		||||
	// This signal is sent once the compositor considers the session to be fully locked.
 | 
			
		||||
| 
						 | 
				
			
			@ -59,6 +59,7 @@ class LockWindowExtension: public QObject {
 | 
			
		|||
public:
 | 
			
		||||
	explicit LockWindowExtension(QObject* parent = nullptr): QObject(parent) {}
 | 
			
		||||
	~LockWindowExtension() override;
 | 
			
		||||
	Q_DISABLE_COPY_MOVE(LockWindowExtension);
 | 
			
		||||
 | 
			
		||||
	// Attach this lock extension to the given window.
 | 
			
		||||
	// The extension is reparented to the window and replaces any existing lock extension.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,7 +7,7 @@
 | 
			
		|||
#include <private/qwaylandwindow_p.h>
 | 
			
		||||
#include <qlogging.h>
 | 
			
		||||
#include <qobject.h>
 | 
			
		||||
#include <wayland-client-protocol.h>
 | 
			
		||||
#include <qtypes.h>
 | 
			
		||||
 | 
			
		||||
#include "lock.hpp"
 | 
			
		||||
#include "session_lock.hpp"
 | 
			
		||||
| 
						 | 
				
			
			@ -27,7 +27,7 @@ QSWaylandSessionLockSurface::QSWaylandSessionLockSurface(QtWaylandClient::QWayla
 | 
			
		|||
		throw nullptr;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	wl_output* output = nullptr;
 | 
			
		||||
	wl_output* output = nullptr; // NOLINT (include)
 | 
			
		||||
	auto* waylandScreen = dynamic_cast<QtWaylandClient::QWaylandScreen*>(qwindow->screen()->handle());
 | 
			
		||||
 | 
			
		||||
	if (waylandScreen != nullptr) {
 | 
			
		||||
| 
						 | 
				
			
			@ -75,7 +75,7 @@ void QSWaylandSessionLockSurface::setExtension(LockWindowExtension* ext) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void QSWaylandSessionLockSurface::setVisible() {
 | 
			
		||||
	if (this->configured && !this->visible) initVisible();
 | 
			
		||||
	if (this->configured && !this->visible) this->initVisible();
 | 
			
		||||
	this->visible = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -92,7 +92,7 @@ void QSWaylandSessionLockSurface::ext_session_lock_surface_v1_configure(
 | 
			
		|||
 | 
			
		||||
		this->window()->resizeFromApplyConfigure(this->size);
 | 
			
		||||
		this->window()->handleExpose(QRect(QPoint(), this->size));
 | 
			
		||||
		if (this->visible) initVisible();
 | 
			
		||||
		if (this->visible) this->initVisible();
 | 
			
		||||
	} else {
 | 
			
		||||
		this->window()->applyConfigureWhenPossible();
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -105,10 +105,10 @@ void QSWaylandSessionLockSurface::initVisible() {
 | 
			
		|||
	// We attach a dummy buffer to satisfy ext_session_lock_v1.
 | 
			
		||||
	this->initBuf = new QtWaylandClient::QWaylandShmBuffer(
 | 
			
		||||
	    this->window()->display(),
 | 
			
		||||
	    size,
 | 
			
		||||
	    this->size,
 | 
			
		||||
	    QImage::Format_ARGB32
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	this->window()->waylandSurface()->attach(initBuf->buffer(), 0, 0);
 | 
			
		||||
	this->window()->waylandSurface()->attach(this->initBuf->buffer(), 0, 0);
 | 
			
		||||
	this->window()->window()->setVisible(true);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,7 +22,7 @@ public:
 | 
			
		|||
	void applyConfigure() override;
 | 
			
		||||
	bool handleExpose(const QRegion& region) override;
 | 
			
		||||
 | 
			
		||||
	void setExtension(LockWindowExtension*);
 | 
			
		||||
	void setExtension(LockWindowExtension* ext);
 | 
			
		||||
	void setVisible();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,14 +5,13 @@
 | 
			
		|||
#include <private/qwaylandwindow_p.h>
 | 
			
		||||
 | 
			
		||||
#include "surface.hpp"
 | 
			
		||||
#include "wayland-wlr-layer-shell-unstable-v1-client-protocol.h"
 | 
			
		||||
 | 
			
		||||
QSWaylandLayerShellIntegration::QSWaylandLayerShellIntegration()
 | 
			
		||||
    : QtWaylandClient::QWaylandShellIntegrationTemplate<QSWaylandLayerShellIntegration>(4) {}
 | 
			
		||||
 | 
			
		||||
QSWaylandLayerShellIntegration::~QSWaylandLayerShellIntegration() {
 | 
			
		||||
	if (this->object() != nullptr) {
 | 
			
		||||
		zwlr_layer_shell_v1_destroy(this->object());
 | 
			
		||||
	if (this->isInitialized()) {
 | 
			
		||||
		this->destroy();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,7 @@
 | 
			
		|||
 | 
			
		||||
#include <private/qwaylandshellintegration_p.h>
 | 
			
		||||
#include <private/qwaylandshellsurface_p.h>
 | 
			
		||||
#include <qtwaylandclientexports.h>
 | 
			
		||||
#include <qtclasshelpermacros.h>
 | 
			
		||||
#include <qwayland-wlr-layer-shell-unstable-v1.h>
 | 
			
		||||
 | 
			
		||||
class QSWaylandLayerShellIntegration
 | 
			
		||||
| 
						 | 
				
			
			@ -11,10 +11,7 @@ class QSWaylandLayerShellIntegration
 | 
			
		|||
public:
 | 
			
		||||
	QSWaylandLayerShellIntegration();
 | 
			
		||||
	~QSWaylandLayerShellIntegration() override;
 | 
			
		||||
	QSWaylandLayerShellIntegration(QSWaylandLayerShellIntegration&&) = delete;
 | 
			
		||||
	QSWaylandLayerShellIntegration(const QSWaylandLayerShellIntegration&) = delete;
 | 
			
		||||
	void operator=(QSWaylandLayerShellIntegration&&) = delete;
 | 
			
		||||
	void operator=(const QSWaylandLayerShellIntegration&) = delete;
 | 
			
		||||
	Q_DISABLE_COPY_MOVE(QSWaylandLayerShellIntegration);
 | 
			
		||||
 | 
			
		||||
	QtWaylandClient::QWaylandShellSurface* createShellSurface(QtWaylandClient::QWaylandWindow* window
 | 
			
		||||
	) override;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,12 +23,11 @@
 | 
			
		|||
[[nodiscard]] QSize constrainedSize(const Anchors& anchors, const QSize& size) noexcept;
 | 
			
		||||
// clang-format on
 | 
			
		||||
 | 
			
		||||
// clang-format off
 | 
			
		||||
QSWaylandLayerSurface::QSWaylandLayerSurface(
 | 
			
		||||
    QSWaylandLayerShellIntegration* shell,
 | 
			
		||||
    QtWaylandClient::QWaylandWindow* window
 | 
			
		||||
): QtWaylandClient::QWaylandShellSurface(window) {
 | 
			
		||||
	// clang-format on
 | 
			
		||||
)
 | 
			
		||||
    : QtWaylandClient::QWaylandShellSurface(window) {
 | 
			
		||||
 | 
			
		||||
	auto* qwindow = window->window();
 | 
			
		||||
	this->ext = LayershellWindowExtension::get(qwindow);
 | 
			
		||||
| 
						 | 
				
			
			@ -37,7 +36,7 @@ QSWaylandLayerSurface::QSWaylandLayerSurface(
 | 
			
		|||
		throw "QSWaylandLayerSurface created with null LayershellWindowExtension";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	wl_output* output = nullptr; // NOLINT (import)
 | 
			
		||||
	wl_output* output = nullptr; // NOLINT (include)
 | 
			
		||||
	if (this->ext->useWindowScreen) {
 | 
			
		||||
		auto* waylandScreen =
 | 
			
		||||
		    dynamic_cast<QtWaylandClient::QWaylandScreen*>(qwindow->screen()->handle());
 | 
			
		||||
| 
						 | 
				
			
			@ -45,8 +44,8 @@ QSWaylandLayerSurface::QSWaylandLayerSurface(
 | 
			
		|||
		if (waylandScreen != nullptr) {
 | 
			
		||||
			output = waylandScreen->output();
 | 
			
		||||
		} else {
 | 
			
		||||
			qWarning() << "Layershell screen is set but does not corrospond to a real screen. Letting "
 | 
			
		||||
			              "the compositor pick.";
 | 
			
		||||
			qWarning(
 | 
			
		||||
			) << "Layershell screen does not corrospond to a real screen. Letting the compositor pick.";
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,7 @@
 | 
			
		|||
 | 
			
		||||
#include <private/qwaylandshellsurface_p.h>
 | 
			
		||||
#include <private/qwaylandwindow_p.h>
 | 
			
		||||
#include <qtclasshelpermacros.h>
 | 
			
		||||
#include <qtwaylandclientexports.h>
 | 
			
		||||
#include <qtypes.h>
 | 
			
		||||
#include <qwayland-wlr-layer-shell-unstable-v1.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -20,10 +21,7 @@ public:
 | 
			
		|||
	);
 | 
			
		||||
 | 
			
		||||
	~QSWaylandLayerSurface() override;
 | 
			
		||||
	QSWaylandLayerSurface(QSWaylandLayerSurface&&) = delete;
 | 
			
		||||
	QSWaylandLayerSurface(const QSWaylandLayerSurface&) = delete;
 | 
			
		||||
	void operator=(QSWaylandLayerSurface&&) = delete;
 | 
			
		||||
	void operator=(const QSWaylandLayerSurface&) = delete;
 | 
			
		||||
	Q_DISABLE_COPY_MOVE(QSWaylandLayerSurface);
 | 
			
		||||
 | 
			
		||||
	[[nodiscard]] bool isExposed() const override;
 | 
			
		||||
	void applyConfigure() override;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -56,17 +56,12 @@ class LayershellWindowExtension: public QObject {
 | 
			
		|||
 | 
			
		||||
public:
 | 
			
		||||
	LayershellWindowExtension(QObject* parent = nullptr): QObject(parent) {}
 | 
			
		||||
	~LayershellWindowExtension() override = default;
 | 
			
		||||
	LayershellWindowExtension(LayershellWindowExtension&&) = delete;
 | 
			
		||||
	LayershellWindowExtension(const LayershellWindowExtension&) = delete;
 | 
			
		||||
	void operator=(LayershellWindowExtension&&) = delete;
 | 
			
		||||
	void operator=(const LayershellWindowExtension&) = delete;
 | 
			
		||||
 | 
			
		||||
	// returns the layershell extension if attached, otherwise nullptr
 | 
			
		||||
	static LayershellWindowExtension* get(QWindow* window);
 | 
			
		||||
 | 
			
		||||
	// Attach this layershell extension to the given window.
 | 
			
		||||
	// The extension is reparented to the window and replaces any existing extensions.
 | 
			
		||||
	// The extension is reparented to the window and replaces any existing layershell extension.
 | 
			
		||||
	// Returns false if the window cannot be used.
 | 
			
		||||
	bool attach(QWindow* window);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue