File indexing completed on 2024-12-01 13:37:30
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez <aleixpol@kde.org> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include "inputpanelv1window.h" 0011 #include "core/output.h" 0012 #include "deleted.h" 0013 #include "inputmethod.h" 0014 #include "wayland/output_interface.h" 0015 #include "wayland/seat_interface.h" 0016 #include "wayland/surface_interface.h" 0017 #include "wayland/textinput_v1_interface.h" 0018 #include "wayland/textinput_v2_interface.h" 0019 #include "wayland/textinput_v3_interface.h" 0020 #include "wayland_server.h" 0021 #include "workspace.h" 0022 0023 using namespace KWaylandServer; 0024 0025 namespace KWin 0026 { 0027 0028 InputPanelV1Window::InputPanelV1Window(InputPanelSurfaceV1Interface *panelSurface) 0029 : WaylandWindow(panelSurface->surface()) 0030 , m_panelSurface(panelSurface) 0031 { 0032 setSkipSwitcher(true); 0033 setSkipPager(true); 0034 setSkipTaskbar(true); 0035 0036 connect(surface(), &SurfaceInterface::aboutToBeDestroyed, this, &InputPanelV1Window::destroyWindow); 0037 connect(surface(), &SurfaceInterface::sizeChanged, this, &InputPanelV1Window::reposition); 0038 connect(surface(), &SurfaceInterface::mapped, this, &InputPanelV1Window::handleMapped); 0039 0040 connect(panelSurface, &InputPanelSurfaceV1Interface::topLevel, this, &InputPanelV1Window::showTopLevel); 0041 connect(panelSurface, &InputPanelSurfaceV1Interface::overlayPanel, this, &InputPanelV1Window::showOverlayPanel); 0042 connect(panelSurface, &InputPanelSurfaceV1Interface::destroyed, this, &InputPanelV1Window::destroyWindow); 0043 0044 connect(workspace(), &Workspace::outputsChanged, this, &InputPanelV1Window::reposition); 0045 0046 kwinApp()->inputMethod()->setPanel(this); 0047 } 0048 0049 void InputPanelV1Window::showOverlayPanel() 0050 { 0051 m_mode = Mode::Overlay; 0052 maybeShow(); 0053 } 0054 0055 void InputPanelV1Window::showTopLevel(OutputInterface *output, InputPanelSurfaceV1Interface::Position position) 0056 { 0057 m_mode = Mode::VirtualKeyboard; 0058 maybeShow(); 0059 } 0060 0061 void InputPanelV1Window::allow() 0062 { 0063 m_allowed = true; 0064 maybeShow(); 0065 } 0066 0067 void InputPanelV1Window::show() 0068 { 0069 m_virtualKeyboardShouldBeShown = true; 0070 maybeShow(); 0071 } 0072 0073 void InputPanelV1Window::hide() 0074 { 0075 m_virtualKeyboardShouldBeShown = false; 0076 if (readyForPainting() && m_mode != Mode::Overlay) { 0077 hideClient(); 0078 } 0079 } 0080 0081 void KWin::InputPanelV1Window::reposition() 0082 { 0083 if (!readyForPainting()) { 0084 return; 0085 } 0086 0087 switch (m_mode) { 0088 case Mode::None: { 0089 // should never happen 0090 }; break; 0091 case Mode::VirtualKeyboard: { 0092 QSizeF panelSize = surface()->size(); 0093 if (!panelSize.isValid() || panelSize.isEmpty()) { 0094 return; 0095 } 0096 0097 const auto activeOutput = workspace()->activeOutput(); 0098 const QRectF outputArea = activeOutput->geometry(); 0099 QRectF availableArea; 0100 if (waylandServer()->isScreenLocked()) { 0101 availableArea = outputArea; 0102 } else { 0103 availableArea = workspace()->clientArea(MaximizeArea, this, activeOutput); 0104 } 0105 0106 panelSize = panelSize.boundedTo(availableArea.size()); 0107 QRectF geo(QPointF(availableArea.left(), availableArea.top() + availableArea.height() - panelSize.height()), panelSize); 0108 geo.translate((availableArea.width() - panelSize.width()) / 2, availableArea.height() - outputArea.height()); 0109 moveResize(geo); 0110 } break; 0111 case Mode::Overlay: { 0112 auto textInputSurface = waylandServer()->seat()->focusedTextInputSurface(); 0113 auto textWindow = waylandServer()->findWindow(textInputSurface); 0114 QRect cursorRectangle; 0115 auto textInputV1 = waylandServer()->seat()->textInputV1(); 0116 if (textInputV1 && textInputV1->isEnabled() && textInputV1->surface() == textInputSurface) { 0117 cursorRectangle = textInputV1->cursorRectangle(); 0118 } 0119 auto textInputV2 = waylandServer()->seat()->textInputV2(); 0120 if (textInputV2 && textInputV2->isEnabled() && textInputV2->surface() == textInputSurface) { 0121 cursorRectangle = textInputV2->cursorRectangle(); 0122 } 0123 auto textInputV3 = waylandServer()->seat()->textInputV3(); 0124 if (textInputV3 && textInputV3->isEnabled() && textInputV3->surface() == textInputSurface) { 0125 cursorRectangle = textInputV3->cursorRectangle(); 0126 } 0127 if (textWindow) { 0128 cursorRectangle.translate(textWindow->bufferGeometry().topLeft().toPoint()); 0129 const QRectF screen = Workspace::self()->clientArea(PlacementArea, this, cursorRectangle.bottomLeft()); 0130 0131 // Reuse the similar logic like xdg popup 0132 QRectF popupRect(popupOffset(cursorRectangle, Qt::BottomEdge | Qt::LeftEdge, Qt::RightEdge | Qt::BottomEdge, surface()->size()), surface()->size()); 0133 0134 if (popupRect.left() < screen.left()) { 0135 popupRect.moveLeft(screen.left()); 0136 } 0137 if (popupRect.right() > screen.right()) { 0138 popupRect.moveRight(screen.right()); 0139 } 0140 if (popupRect.top() < screen.top() || popupRect.bottom() > screen.bottom()) { 0141 auto flippedPopupRect = 0142 QRectF(popupOffset(cursorRectangle, Qt::TopEdge | Qt::LeftEdge, Qt::RightEdge | Qt::TopEdge, surface()->size()), surface()->size()); 0143 0144 // if it still doesn't fit we should continue with the unflipped version 0145 if (flippedPopupRect.top() >= screen.top() || flippedPopupRect.bottom() <= screen.bottom()) { 0146 popupRect.moveTop(flippedPopupRect.top()); 0147 } 0148 } 0149 moveResize(popupRect); 0150 } 0151 } break; 0152 } 0153 } 0154 0155 void InputPanelV1Window::destroyWindow() 0156 { 0157 markAsZombie(); 0158 0159 Deleted *deleted = Deleted::create(this); 0160 Q_EMIT windowClosed(this, deleted); 0161 StackingUpdatesBlocker blocker(workspace()); 0162 waylandServer()->removeWindow(this); 0163 deleted->unrefWindow(); 0164 0165 delete this; 0166 } 0167 0168 NET::WindowType InputPanelV1Window::windowType(bool, int) const 0169 { 0170 return NET::Utility; 0171 } 0172 0173 QRectF InputPanelV1Window::inputGeometry() const 0174 { 0175 return readyForPainting() ? QRectF(surface()->input().boundingRect()).translated(pos()) : QRectF(); 0176 } 0177 0178 void InputPanelV1Window::moveResizeInternal(const QRectF &rect, MoveResizeMode mode) 0179 { 0180 updateGeometry(rect); 0181 } 0182 0183 void InputPanelV1Window::handleMapped() 0184 { 0185 updateDepth(); 0186 maybeShow(); 0187 } 0188 0189 void InputPanelV1Window::maybeShow() 0190 { 0191 const bool shouldShow = m_mode == Mode::Overlay || (m_mode == Mode::VirtualKeyboard && m_allowed && m_virtualKeyboardShouldBeShown); 0192 if (shouldShow && !isZombie() && surface()->isMapped()) { 0193 setReadyForPainting(); 0194 reposition(); 0195 showClient(); 0196 } 0197 } 0198 0199 } // namespace KWin