2020-02-17 18:39:17 +00:00
|
|
|
/*
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-FileCopyrightText: 2015 Martin Flöser <mgraesslin@kde.org>
|
|
|
|
SPDX-FileCopyrightText: 2018 David Edmundson <davidedmundson@kde.org>
|
|
|
|
SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
|
|
|
|
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
*/
|
2020-02-17 18:39:17 +00:00
|
|
|
|
2022-04-22 17:51:07 +00:00
|
|
|
#include "waylandwindow.h"
|
2022-12-15 20:35:22 +00:00
|
|
|
#include "scene/windowitem.h"
|
2022-04-22 09:27:33 +00:00
|
|
|
#include "wayland/clientconnection.h"
|
|
|
|
#include "wayland/display.h"
|
|
|
|
#include "wayland/surface_interface.h"
|
2020-02-17 18:39:17 +00:00
|
|
|
#include "wayland_server.h"
|
|
|
|
#include "workspace.h"
|
|
|
|
|
|
|
|
#include <QFileInfo>
|
|
|
|
|
|
|
|
#include <csignal>
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
using namespace KWaylandServer;
|
|
|
|
|
|
|
|
namespace KWin
|
|
|
|
{
|
|
|
|
|
2020-08-18 12:51:20 +00:00
|
|
|
enum WaylandGeometryType {
|
|
|
|
WaylandGeometryClient = 0x1,
|
|
|
|
WaylandGeometryFrame = 0x2,
|
|
|
|
WaylandGeometryBuffer = 0x4,
|
|
|
|
};
|
|
|
|
Q_DECLARE_FLAGS(WaylandGeometryTypes, WaylandGeometryType)
|
|
|
|
|
2022-04-22 17:51:07 +00:00
|
|
|
WaylandWindow::WaylandWindow(SurfaceInterface *surface)
|
2023-03-21 08:53:21 +00:00
|
|
|
: m_isScreenLocker(surface->client() == waylandServer()->screenLockerClientConnection())
|
2020-02-17 18:39:17 +00:00
|
|
|
{
|
|
|
|
setSurface(surface);
|
|
|
|
|
2020-09-25 06:57:28 +00:00
|
|
|
connect(surface, &SurfaceInterface::shadowChanged,
|
2022-04-22 17:51:07 +00:00
|
|
|
this, &WaylandWindow::updateShadow);
|
|
|
|
connect(this, &WaylandWindow::frameGeometryChanged,
|
|
|
|
this, &WaylandWindow::updateClientOutputs);
|
|
|
|
connect(this, &WaylandWindow::desktopFileNameChanged,
|
|
|
|
this, &WaylandWindow::updateIcon);
|
2022-10-19 07:10:03 +00:00
|
|
|
connect(workspace(), &Workspace::outputsChanged, this, &WaylandWindow::updateClientOutputs);
|
2020-02-17 18:39:17 +00:00
|
|
|
|
|
|
|
updateResourceName();
|
|
|
|
updateIcon();
|
|
|
|
}
|
|
|
|
|
2023-01-07 20:51:34 +00:00
|
|
|
std::unique_ptr<WindowItem> WaylandWindow::createItem(Scene *scene)
|
2022-05-09 15:47:12 +00:00
|
|
|
{
|
2023-01-07 20:51:34 +00:00
|
|
|
return std::make_unique<WindowItemWayland>(this, scene);
|
2022-05-09 15:47:12 +00:00
|
|
|
}
|
|
|
|
|
2022-04-22 17:51:07 +00:00
|
|
|
QString WaylandWindow::captionNormal() const
|
2020-02-17 18:39:17 +00:00
|
|
|
{
|
|
|
|
return m_captionNormal;
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:51:07 +00:00
|
|
|
QString WaylandWindow::captionSuffix() const
|
2020-02-17 18:39:17 +00:00
|
|
|
{
|
|
|
|
return m_captionSuffix;
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:51:07 +00:00
|
|
|
pid_t WaylandWindow::pid() const
|
2020-02-17 18:39:17 +00:00
|
|
|
{
|
2023-03-21 08:53:21 +00:00
|
|
|
return surface() ? surface()->client()->processId() : -1;
|
2020-02-17 18:39:17 +00:00
|
|
|
}
|
|
|
|
|
2022-04-22 17:51:07 +00:00
|
|
|
bool WaylandWindow::isClient() const
|
2021-05-25 16:06:17 +00:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:51:07 +00:00
|
|
|
bool WaylandWindow::isLockScreen() const
|
2020-02-17 18:39:17 +00:00
|
|
|
{
|
2023-03-21 08:53:21 +00:00
|
|
|
return m_isScreenLocker;
|
2020-02-17 18:39:17 +00:00
|
|
|
}
|
|
|
|
|
2022-04-22 17:51:07 +00:00
|
|
|
bool WaylandWindow::isLocalhost() const
|
2020-02-17 18:39:17 +00:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:51:07 +00:00
|
|
|
Window *WaylandWindow::findModal(bool allow_itself)
|
2020-02-17 18:39:17 +00:00
|
|
|
{
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2022-08-29 13:54:28 +00:00
|
|
|
QRectF WaylandWindow::resizeWithChecks(const QRectF &geometry, const QSizeF &size)
|
2020-02-17 18:39:17 +00:00
|
|
|
{
|
2022-08-29 13:54:28 +00:00
|
|
|
const QRectF area = workspace()->clientArea(WorkArea, this, geometry.center());
|
2020-02-17 18:39:17 +00:00
|
|
|
|
2022-05-16 20:13:39 +00:00
|
|
|
qreal width = size.width();
|
|
|
|
qreal height = size.height();
|
2020-02-17 18:39:17 +00:00
|
|
|
|
|
|
|
// don't allow growing larger than workarea
|
|
|
|
if (width > area.width()) {
|
|
|
|
width = area.width();
|
|
|
|
}
|
|
|
|
if (height > area.height()) {
|
|
|
|
height = area.height();
|
|
|
|
}
|
2022-08-29 13:54:28 +00:00
|
|
|
return QRectF(geometry.topLeft(), QSizeF(width, height));
|
2020-02-17 18:39:17 +00:00
|
|
|
}
|
|
|
|
|
2022-04-22 17:51:07 +00:00
|
|
|
void WaylandWindow::killWindow()
|
2020-02-17 18:39:17 +00:00
|
|
|
{
|
|
|
|
if (!surface()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
auto c = surface()->client();
|
|
|
|
if (c->processId() == getpid() || c->processId() == 0) {
|
|
|
|
c->destroy();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
::kill(c->processId(), SIGTERM);
|
|
|
|
// give it time to terminate and only if terminate fails, try destroy Wayland connection
|
|
|
|
QTimer::singleShot(5000, c, &ClientConnection::destroy);
|
|
|
|
}
|
|
|
|
|
2022-10-21 14:24:42 +00:00
|
|
|
QString WaylandWindow::windowRole() const
|
2020-02-17 18:39:17 +00:00
|
|
|
{
|
2022-10-21 14:24:42 +00:00
|
|
|
return QString();
|
2020-02-17 18:39:17 +00:00
|
|
|
}
|
|
|
|
|
2022-04-22 17:51:07 +00:00
|
|
|
bool WaylandWindow::belongsToSameApplication(const Window *other, SameApplicationChecks checks) const
|
2020-02-17 18:39:17 +00:00
|
|
|
{
|
|
|
|
if (checks.testFlag(SameApplicationCheck::AllowCrossProcesses)) {
|
|
|
|
if (other->desktopFileName() == desktopFileName()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (auto s = other->surface()) {
|
|
|
|
return s->client() == surface()->client();
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:51:07 +00:00
|
|
|
bool WaylandWindow::belongsToDesktop() const
|
2020-02-17 18:39:17 +00:00
|
|
|
{
|
2022-04-23 08:33:23 +00:00
|
|
|
const auto clients = waylandServer()->windows();
|
2020-02-17 18:39:17 +00:00
|
|
|
|
|
|
|
return std::any_of(clients.constBegin(), clients.constEnd(),
|
2022-04-22 17:39:12 +00:00
|
|
|
[this](const Window *client) {
|
2022-03-23 10:13:38 +00:00
|
|
|
if (belongsToSameApplication(client, SameApplicationChecks())) {
|
|
|
|
return client->isDesktop();
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
});
|
2020-02-17 18:39:17 +00:00
|
|
|
}
|
|
|
|
|
2022-04-22 17:51:07 +00:00
|
|
|
void WaylandWindow::updateClientOutputs()
|
2020-02-17 18:39:17 +00:00
|
|
|
{
|
2023-03-30 15:49:31 +00:00
|
|
|
if (isDeleted()) {
|
|
|
|
return;
|
|
|
|
}
|
2022-05-16 20:13:39 +00:00
|
|
|
surface()->setOutputs(waylandServer()->display()->outputsIntersecting(frameGeometry().toAlignedRect()));
|
2022-06-30 22:23:35 +00:00
|
|
|
if (output()) {
|
2023-02-08 20:32:28 +00:00
|
|
|
surface()->setPreferredBufferScale(output()->scale());
|
2022-06-30 22:23:35 +00:00
|
|
|
}
|
2020-02-17 18:39:17 +00:00
|
|
|
}
|
|
|
|
|
2022-04-22 17:51:07 +00:00
|
|
|
void WaylandWindow::updateIcon()
|
2020-02-17 18:39:17 +00:00
|
|
|
{
|
|
|
|
const QString waylandIconName = QStringLiteral("wayland");
|
|
|
|
const QString dfIconName = iconFromDesktopFile();
|
|
|
|
const QString iconName = dfIconName.isEmpty() ? waylandIconName : dfIconName;
|
|
|
|
if (iconName == icon().name()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
setIcon(QIcon::fromTheme(iconName));
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:51:07 +00:00
|
|
|
void WaylandWindow::updateResourceName()
|
2020-02-17 18:39:17 +00:00
|
|
|
{
|
|
|
|
const QFileInfo fileInfo(surface()->client()->executablePath());
|
|
|
|
if (fileInfo.exists()) {
|
2021-04-19 07:50:41 +00:00
|
|
|
const QByteArray executableFileName = fileInfo.fileName().toUtf8();
|
|
|
|
setResourceClass(executableFileName, executableFileName);
|
2020-02-17 18:39:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:51:07 +00:00
|
|
|
void WaylandWindow::updateCaption()
|
2020-02-17 18:39:17 +00:00
|
|
|
{
|
|
|
|
const QString oldSuffix = m_captionSuffix;
|
|
|
|
const auto shortcut = shortcutCaptionSuffix();
|
|
|
|
m_captionSuffix = shortcut;
|
2022-04-23 08:33:23 +00:00
|
|
|
if ((!isSpecialWindow() || isToolbar()) && findWindowWithSameCaption()) {
|
2020-02-17 18:39:17 +00:00
|
|
|
int i = 2;
|
|
|
|
do {
|
|
|
|
m_captionSuffix = shortcut + QLatin1String(" <") + QString::number(i) + QLatin1Char('>');
|
|
|
|
i++;
|
2022-04-23 08:33:23 +00:00
|
|
|
} while (findWindowWithSameCaption());
|
2020-02-17 18:39:17 +00:00
|
|
|
}
|
|
|
|
if (m_captionSuffix != oldSuffix) {
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT captionChanged();
|
2020-02-17 18:39:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:51:07 +00:00
|
|
|
void WaylandWindow::setCaption(const QString &caption)
|
2020-02-17 18:39:17 +00:00
|
|
|
{
|
|
|
|
const QString oldSuffix = m_captionSuffix;
|
|
|
|
m_captionNormal = caption.simplified();
|
|
|
|
updateCaption();
|
|
|
|
if (m_captionSuffix == oldSuffix) {
|
|
|
|
// Don't emit caption change twice it already got emitted by the changing suffix.
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT captionChanged();
|
2020-02-17 18:39:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:51:07 +00:00
|
|
|
void WaylandWindow::doSetActive()
|
2020-02-17 18:39:17 +00:00
|
|
|
{
|
|
|
|
if (isActive()) { // TODO: Xwayland clients must be unfocused somewhere else.
|
|
|
|
StackingUpdatesBlocker blocker(workspace());
|
|
|
|
workspace()->focusToNull();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:51:07 +00:00
|
|
|
void WaylandWindow::cleanGrouping()
|
2020-08-19 08:01:33 +00:00
|
|
|
{
|
2023-03-23 08:19:12 +00:00
|
|
|
// We want to break parent-child relationships, but preserve stacking
|
|
|
|
// order constraints at the same time for window closing animations.
|
|
|
|
|
2020-08-19 08:01:33 +00:00
|
|
|
if (transientFor()) {
|
2023-03-23 08:19:12 +00:00
|
|
|
transientFor()->removeTransientFromList(this);
|
|
|
|
setTransientFor(nullptr);
|
2020-08-19 08:01:33 +00:00
|
|
|
}
|
2023-03-23 08:19:12 +00:00
|
|
|
|
|
|
|
const auto children = transients();
|
|
|
|
for (Window *transient : children) {
|
|
|
|
removeTransientFromList(transient);
|
|
|
|
transient->setTransientFor(nullptr);
|
2020-08-19 08:01:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:51:07 +00:00
|
|
|
bool WaylandWindow::isShown() const
|
2020-08-17 08:24:18 +00:00
|
|
|
{
|
Drop Deleted
Currently, the normal window lifecycle looks as follows: create Window,
wait until it's shown, add it to Workspace, wait until it's closed,
create a Deleted, copy properties from the original window to the
deleted one, destroy the original window, wait until the last deleted
window reference is dropped.
There are a couple of issues with this design: we can't nicely
encapsulate X11 or Wayland specific implementation details if they need
to be accessed for closed windows; manual copying of properties is
cumbersome and error prone and we've had a dozen of cases where effects
worked incorrectly because some properties had not been copied.
The goal of this patch is to drop Deleted and extend the lifetime of the
original window, but with a special state set: Window::isDeleted().
The main danger is that somebody can try to do something with deleted
windows that they should not do, but on the other hand, such code needs
to be guarded with relevant checks too.
2023-03-14 09:45:18 +00:00
|
|
|
return !isDeleted() && !isHidden() && !isMinimized();
|
2020-08-17 08:24:18 +00:00
|
|
|
}
|
|
|
|
|
2022-04-22 17:51:07 +00:00
|
|
|
bool WaylandWindow::isHiddenInternal() const
|
2020-08-17 08:24:18 +00:00
|
|
|
{
|
|
|
|
return isHidden();
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:51:07 +00:00
|
|
|
bool WaylandWindow::isHidden() const
|
2020-08-17 08:24:18 +00:00
|
|
|
{
|
|
|
|
return m_isHidden;
|
|
|
|
}
|
|
|
|
|
2022-04-22 17:51:07 +00:00
|
|
|
void WaylandWindow::showClient()
|
2020-08-17 08:24:18 +00:00
|
|
|
{
|
|
|
|
if (!isHidden()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
m_isHidden = false;
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT windowShown(this);
|
2020-08-17 08:24:18 +00:00
|
|
|
}
|
|
|
|
|
2022-04-22 17:51:07 +00:00
|
|
|
void WaylandWindow::hideClient()
|
2020-08-17 08:24:18 +00:00
|
|
|
{
|
|
|
|
if (isHidden()) {
|
|
|
|
return;
|
|
|
|
}
|
2021-04-30 18:06:58 +00:00
|
|
|
if (isInteractiveMoveResize()) {
|
|
|
|
leaveInteractiveMoveResize();
|
2020-08-17 08:24:18 +00:00
|
|
|
}
|
|
|
|
m_isHidden = true;
|
2022-04-23 08:33:23 +00:00
|
|
|
workspace()->windowHidden(this);
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT windowHidden(this);
|
2020-08-17 08:24:18 +00:00
|
|
|
}
|
|
|
|
|
2022-05-16 20:13:39 +00:00
|
|
|
QRectF WaylandWindow::frameRectToBufferRect(const QRectF &rect) const
|
2020-08-18 12:51:20 +00:00
|
|
|
{
|
2022-05-16 20:13:39 +00:00
|
|
|
return QRectF(rect.topLeft(), surface()->size());
|
2020-08-18 12:51:20 +00:00
|
|
|
}
|
|
|
|
|
2022-05-16 20:13:39 +00:00
|
|
|
void WaylandWindow::updateGeometry(const QRectF &rect)
|
2020-08-18 12:51:20 +00:00
|
|
|
{
|
2022-05-16 20:13:39 +00:00
|
|
|
const QRectF oldClientGeometry = m_clientGeometry;
|
|
|
|
const QRectF oldFrameGeometry = m_frameGeometry;
|
|
|
|
const QRectF oldBufferGeometry = m_bufferGeometry;
|
2022-04-14 12:33:28 +00:00
|
|
|
const Output *oldOutput = m_output;
|
2020-08-18 12:51:20 +00:00
|
|
|
|
|
|
|
m_clientGeometry = frameRectToClientRect(rect);
|
|
|
|
m_frameGeometry = rect;
|
|
|
|
m_bufferGeometry = frameRectToBufferRect(rect);
|
|
|
|
|
|
|
|
WaylandGeometryTypes changedGeometries;
|
|
|
|
|
|
|
|
if (m_clientGeometry != oldClientGeometry) {
|
|
|
|
changedGeometries |= WaylandGeometryClient;
|
|
|
|
}
|
|
|
|
if (m_frameGeometry != oldFrameGeometry) {
|
|
|
|
changedGeometries |= WaylandGeometryFrame;
|
|
|
|
}
|
|
|
|
if (m_bufferGeometry != oldBufferGeometry) {
|
|
|
|
changedGeometries |= WaylandGeometryBuffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!changedGeometries) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-07-11 10:41:15 +00:00
|
|
|
m_output = workspace()->outputAt(rect.center());
|
2020-08-18 12:51:20 +00:00
|
|
|
updateWindowRules(Rules::Position | Rules::Size);
|
|
|
|
|
|
|
|
if (changedGeometries & WaylandGeometryBuffer) {
|
2023-02-25 22:29:07 +00:00
|
|
|
Q_EMIT bufferGeometryChanged(oldBufferGeometry);
|
2020-08-18 12:51:20 +00:00
|
|
|
}
|
|
|
|
if (changedGeometries & WaylandGeometryClient) {
|
2023-02-25 22:29:07 +00:00
|
|
|
Q_EMIT clientGeometryChanged(oldClientGeometry);
|
2020-08-18 12:51:20 +00:00
|
|
|
}
|
|
|
|
if (changedGeometries & WaylandGeometryFrame) {
|
2023-02-25 22:29:07 +00:00
|
|
|
Q_EMIT frameGeometryChanged(oldFrameGeometry);
|
2020-08-18 12:51:20 +00:00
|
|
|
}
|
2021-11-26 10:03:14 +00:00
|
|
|
if (oldOutput != m_output) {
|
2023-02-08 20:17:27 +00:00
|
|
|
Q_EMIT outputChanged();
|
2021-11-26 10:03:14 +00:00
|
|
|
}
|
2023-02-25 22:29:07 +00:00
|
|
|
Q_EMIT geometryShapeChanged(oldFrameGeometry);
|
2020-08-18 12:51:20 +00:00
|
|
|
}
|
|
|
|
|
2023-03-02 15:02:09 +00:00
|
|
|
void WaylandWindow::markAsMapped()
|
|
|
|
{
|
|
|
|
if (Q_UNLIKELY(!ready_for_painting)) {
|
|
|
|
setupCompositing();
|
|
|
|
setReadyForPainting();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-17 18:39:17 +00:00
|
|
|
} // namespace KWin
|