kwin/inputpanelv1client.cpp
Vlad Zahorodnii 41d431de27 Adapt to input region changes in kwayland-server
SurfaceInterface::inputIsInfinite() has been dropped. If the surface has
no any input region specified, SurfaceInterface::input() will return a
region that corresponds to the rect of the surface (0, 0, width, height).

While the new design is more robust, for example it's no longer possible
to forget to check SurfaceInterface::inputIsInfinite(), it has shown some
issues in the input stack of kwin.

Currently, acceptsInput() will return false if you attempt to click the
server-side decoration for a surface whose input region is not empty.

Therefore, it's possible for an application to set an input region with
a width and a height of 1. If user doesn't know about KSysGuard or the
possibility of closing apps via the task manager, they won't be able to
close such an application.

Another issue is that if an application has specified an empty input
region on purpose, user will be still able click it. With the new
behavior of SurfaceInterface::input(), this is no longer an issue and it
is handled properly by kwin.
2020-10-17 16:11:13 +03:00

126 lines
4 KiB
C++

/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez <aleixpol@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "inputpanelv1client.h"
#include "deleted.h"
#include "wayland_server.h"
#include "workspace.h"
#include "abstract_wayland_output.h"
#include "platform.h"
#include <KWaylandServer/output_interface.h>
#include <KWaylandServer/seat_interface.h>
#include <KWaylandServer/surface_interface.h>
#include <KWaylandServer/textinput_v2_interface.h>
using namespace KWaylandServer;
namespace KWin
{
InputPanelV1Client::InputPanelV1Client(InputPanelSurfaceV1Interface *panelSurface)
: WaylandClient(panelSurface->surface())
, m_panelSurface(panelSurface)
{
setSkipSwitcher(true);
setSkipPager(true);
setSkipTaskbar(true);
setPositionSyncMode(SyncMode::Sync);
setSizeSyncMode(SyncMode::Sync);
connect(surface(), &SurfaceInterface::aboutToBeDestroyed, this, &InputPanelV1Client::destroyClient);
connect(surface(), &SurfaceInterface::sizeChanged, this, &InputPanelV1Client::reposition);
connect(surface(), &SurfaceInterface::mapped, this, &InputPanelV1Client::updateDepth);
connect(surface(), &SurfaceInterface::damaged, this, QOverload<const QRegion &>::of(&WaylandClient::addRepaint));
connect(panelSurface, &InputPanelSurfaceV1Interface::topLevel, this, &InputPanelV1Client::showTopLevel);
connect(panelSurface, &InputPanelSurfaceV1Interface::overlayPanel, this, &InputPanelV1Client::showOverlayPanel);
connect(panelSurface, &InputPanelSurfaceV1Interface::destroyed, this, &InputPanelV1Client::destroyClient);
}
void InputPanelV1Client::showOverlayPanel()
{
setOutput(nullptr);
m_mode = Overlay;
reposition();
setReadyForPainting();
}
void InputPanelV1Client::showTopLevel(OutputInterface *output, InputPanelSurfaceV1Interface::Position position)
{
Q_UNUSED(position);
m_mode = Toplevel;
setOutput(output);
reposition();
setReadyForPainting();
}
void KWin::InputPanelV1Client::reposition()
{
switch (m_mode) {
case Toplevel: {
if (m_output) {
const QSize panelSize = surface()->size();
if (!panelSize.isValid() || panelSize.isEmpty()) {
return;
}
const auto outputGeometry = m_output->geometry();
QRect geo(outputGeometry.topLeft(), panelSize);
geo.translate((outputGeometry.width() - panelSize.width())/2, outputGeometry.height() - panelSize.height());
updateGeometry(geo);
}
} break;
case Overlay: {
auto textClient = waylandServer()->findClient(waylandServer()->seat()->focusedTextInputSurface());
auto textInput = waylandServer()->seat()->textInputV2();
if (textClient && textInput) {
const auto cursorRectangle = textInput->cursorRectangle();
updateGeometry({textClient->pos() + textClient->clientPos() + cursorRectangle.bottomLeft(), surface()->size()});
}
} break;
}
}
void InputPanelV1Client::destroyClient()
{
markAsZombie();
Deleted *deleted = Deleted::create(this);
emit windowClosed(this, deleted);
StackingUpdatesBlocker blocker(workspace());
waylandServer()->removeClient(this);
deleted->unrefWindow();
delete this;
}
NET::WindowType InputPanelV1Client::windowType(bool, int) const
{
return NET::Utility;
}
QRect InputPanelV1Client::inputGeometry() const
{
return surface()->input().boundingRect().translated(pos());
}
void InputPanelV1Client::setOutput(OutputInterface *outputIface)
{
if (m_output) {
disconnect(m_output, &AbstractWaylandOutput::geometryChanged, this, &InputPanelV1Client::reposition);
}
m_output = waylandServer()->findOutput(outputIface);
if (m_output) {
connect(m_output, &AbstractWaylandOutput::geometryChanged, this, &InputPanelV1Client::reposition);
}
}
} // namespace KWin