176ae6e692
Practically all code assumes that the window type is static and fixing it would bring extra complexity, which may not be worth given that there are window rules to control position, focus, layer, etc. BUG: 466016
206 lines
6.7 KiB
C++
206 lines
6.7 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 "inputpanelv1window.h"
|
|
#include "core/output.h"
|
|
#include "inputmethod.h"
|
|
#include "wayland/output.h"
|
|
#include "wayland/seat.h"
|
|
#include "wayland/surface.h"
|
|
#include "wayland/textinput_v1.h"
|
|
#include "wayland/textinput_v2.h"
|
|
#include "wayland/textinput_v3.h"
|
|
#include "wayland_server.h"
|
|
#include "workspace.h"
|
|
|
|
namespace KWin
|
|
{
|
|
|
|
InputPanelV1Window::InputPanelV1Window(InputPanelSurfaceV1Interface *panelSurface)
|
|
: WaylandWindow(panelSurface->surface())
|
|
, m_panelSurface(panelSurface)
|
|
{
|
|
setSkipSwitcher(true);
|
|
setSkipPager(true);
|
|
setSkipTaskbar(true);
|
|
|
|
connect(surface(), &SurfaceInterface::aboutToBeDestroyed, this, &InputPanelV1Window::destroyWindow);
|
|
connect(surface(), &SurfaceInterface::sizeChanged, this, &InputPanelV1Window::reposition);
|
|
connect(surface(), &SurfaceInterface::inputChanged, this, &InputPanelV1Window::reposition);
|
|
connect(surface(), &SurfaceInterface::mapped, this, &InputPanelV1Window::handleMapped);
|
|
|
|
connect(panelSurface, &InputPanelSurfaceV1Interface::topLevel, this, &InputPanelV1Window::showTopLevel);
|
|
connect(panelSurface, &InputPanelSurfaceV1Interface::overlayPanel, this, &InputPanelV1Window::showOverlayPanel);
|
|
connect(panelSurface, &InputPanelSurfaceV1Interface::aboutToBeDestroyed, this, &InputPanelV1Window::destroyWindow);
|
|
|
|
connect(workspace(), &Workspace::outputsChanged, this, &InputPanelV1Window::reposition);
|
|
|
|
kwinApp()->inputMethod()->setPanel(this);
|
|
}
|
|
|
|
void InputPanelV1Window::showOverlayPanel()
|
|
{
|
|
m_mode = Mode::Overlay;
|
|
maybeShow();
|
|
}
|
|
|
|
void InputPanelV1Window::showTopLevel(OutputInterface *output, InputPanelSurfaceV1Interface::Position position)
|
|
{
|
|
m_mode = Mode::VirtualKeyboard;
|
|
maybeShow();
|
|
}
|
|
|
|
void InputPanelV1Window::allow()
|
|
{
|
|
m_allowed = true;
|
|
maybeShow();
|
|
}
|
|
|
|
void InputPanelV1Window::show()
|
|
{
|
|
m_virtualKeyboardShouldBeShown = true;
|
|
maybeShow();
|
|
}
|
|
|
|
void InputPanelV1Window::hide()
|
|
{
|
|
m_virtualKeyboardShouldBeShown = false;
|
|
if (readyForPainting() && m_mode != Mode::Overlay) {
|
|
setHidden(true);
|
|
}
|
|
}
|
|
|
|
void InputPanelV1Window::reposition()
|
|
{
|
|
if (!readyForPainting()) {
|
|
return;
|
|
}
|
|
|
|
switch (m_mode) {
|
|
case Mode::None: {
|
|
// should never happen
|
|
}; break;
|
|
case Mode::VirtualKeyboard: {
|
|
// maliit creates a fullscreen overlay so use the input shape as the window geometry.
|
|
m_windowGeometry = surface()->input().boundingRect();
|
|
|
|
const auto activeOutput = workspace()->activeOutput();
|
|
QRectF availableArea;
|
|
if (waylandServer()->isScreenLocked()) {
|
|
availableArea = workspace()->clientArea(FullScreenArea, this, activeOutput);
|
|
} else {
|
|
availableArea = workspace()->clientArea(MaximizeArea, this, activeOutput);
|
|
}
|
|
|
|
QRectF geo = m_windowGeometry;
|
|
geo.moveLeft(availableArea.left() + (availableArea.width() - geo.width()) / 2);
|
|
geo.moveBottom(availableArea.bottom());
|
|
|
|
moveResize(geo);
|
|
} break;
|
|
case Mode::Overlay: {
|
|
auto textInputSurface = waylandServer()->seat()->focusedTextInputSurface();
|
|
auto textWindow = waylandServer()->findWindow(textInputSurface);
|
|
QRect cursorRectangle;
|
|
auto textInputV1 = waylandServer()->seat()->textInputV1();
|
|
if (textInputV1 && textInputV1->isEnabled() && textInputV1->surface() == textInputSurface) {
|
|
cursorRectangle = textInputV1->cursorRectangle();
|
|
}
|
|
auto textInputV2 = waylandServer()->seat()->textInputV2();
|
|
if (textInputV2 && textInputV2->isEnabled() && textInputV2->surface() == textInputSurface) {
|
|
cursorRectangle = textInputV2->cursorRectangle();
|
|
}
|
|
auto textInputV3 = waylandServer()->seat()->textInputV3();
|
|
if (textInputV3 && textInputV3->isEnabled() && textInputV3->surface() == textInputSurface) {
|
|
cursorRectangle = textInputV3->cursorRectangle();
|
|
}
|
|
if (textWindow) {
|
|
cursorRectangle.translate(textWindow->bufferGeometry().topLeft().toPoint());
|
|
const QRectF screen = Workspace::self()->clientArea(PlacementArea, this, cursorRectangle.bottomLeft());
|
|
|
|
m_windowGeometry = QRectF(QPointF(0, 0), surface()->size());
|
|
|
|
// Reuse the similar logic like xdg popup
|
|
QRectF popupRect(popupOffset(cursorRectangle, Qt::BottomEdge | Qt::LeftEdge, Qt::RightEdge | Qt::BottomEdge, m_windowGeometry.size()), m_windowGeometry.size());
|
|
|
|
if (popupRect.left() < screen.left()) {
|
|
popupRect.moveLeft(screen.left());
|
|
}
|
|
if (popupRect.right() > screen.right()) {
|
|
popupRect.moveRight(screen.right());
|
|
}
|
|
if (popupRect.top() < screen.top() || popupRect.bottom() > screen.bottom()) {
|
|
auto flippedPopupRect =
|
|
QRectF(popupOffset(cursorRectangle, Qt::TopEdge | Qt::LeftEdge, Qt::RightEdge | Qt::TopEdge, m_windowGeometry.size()), m_windowGeometry.size());
|
|
|
|
// if it still doesn't fit we should continue with the unflipped version
|
|
if (flippedPopupRect.top() >= screen.top() && flippedPopupRect.bottom() <= screen.bottom()) {
|
|
popupRect.moveTop(flippedPopupRect.top());
|
|
}
|
|
}
|
|
if (popupRect.top() < screen.top()) {
|
|
popupRect.moveTop(screen.top());
|
|
}
|
|
if (popupRect.bottom() > screen.bottom()) {
|
|
popupRect.moveBottom(screen.bottom());
|
|
}
|
|
|
|
moveResize(popupRect);
|
|
}
|
|
} break;
|
|
}
|
|
}
|
|
|
|
void InputPanelV1Window::destroyWindow()
|
|
{
|
|
m_panelSurface->disconnect(this);
|
|
m_panelSurface->surface()->disconnect(this);
|
|
|
|
markAsDeleted();
|
|
|
|
Q_EMIT closed();
|
|
StackingUpdatesBlocker blocker(workspace());
|
|
waylandServer()->removeWindow(this);
|
|
|
|
unref();
|
|
}
|
|
|
|
NET::WindowType InputPanelV1Window::windowType() const
|
|
{
|
|
return NET::Utility;
|
|
}
|
|
|
|
QRectF InputPanelV1Window::frameRectToBufferRect(const QRectF &rect) const
|
|
{
|
|
return QRectF(rect.topLeft() - m_windowGeometry.topLeft(), surface()->size());
|
|
}
|
|
|
|
void InputPanelV1Window::moveResizeInternal(const QRectF &rect, MoveResizeMode mode)
|
|
{
|
|
updateGeometry(rect);
|
|
}
|
|
|
|
void InputPanelV1Window::handleMapped()
|
|
{
|
|
maybeShow();
|
|
}
|
|
|
|
void InputPanelV1Window::maybeShow()
|
|
{
|
|
const bool shouldShow = m_mode == Mode::Overlay || (m_mode == Mode::VirtualKeyboard && m_allowed && m_virtualKeyboardShouldBeShown);
|
|
if (shouldShow && !isDeleted() && surface()->isMapped()) {
|
|
markAsMapped();
|
|
reposition();
|
|
setHidden(false);
|
|
}
|
|
}
|
|
|
|
} // namespace KWin
|
|
|
|
#include "moc_inputpanelv1window.cpp"
|