File indexing completed on 2024-11-10 04:57:48
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 "inputmethod.h" 0013 #include "wayland/output.h" 0014 #include "wayland/seat.h" 0015 #include "wayland/surface.h" 0016 #include "wayland/textinput_v1.h" 0017 #include "wayland/textinput_v2.h" 0018 #include "wayland/textinput_v3.h" 0019 #include "wayland_server.h" 0020 #include "workspace.h" 0021 0022 namespace KWin 0023 { 0024 0025 InputPanelV1Window::InputPanelV1Window(InputPanelSurfaceV1Interface *panelSurface) 0026 : WaylandWindow(panelSurface->surface()) 0027 , m_panelSurface(panelSurface) 0028 { 0029 setSkipSwitcher(true); 0030 setSkipPager(true); 0031 setSkipTaskbar(true); 0032 0033 connect(surface(), &SurfaceInterface::aboutToBeDestroyed, this, &InputPanelV1Window::destroyWindow); 0034 connect(surface(), &SurfaceInterface::sizeChanged, this, &InputPanelV1Window::reposition); 0035 connect(surface(), &SurfaceInterface::inputChanged, this, &InputPanelV1Window::reposition); 0036 connect(surface(), &SurfaceInterface::mapped, this, &InputPanelV1Window::handleMapped); 0037 0038 connect(panelSurface, &InputPanelSurfaceV1Interface::topLevel, this, &InputPanelV1Window::showTopLevel); 0039 connect(panelSurface, &InputPanelSurfaceV1Interface::overlayPanel, this, &InputPanelV1Window::showOverlayPanel); 0040 connect(panelSurface, &InputPanelSurfaceV1Interface::aboutToBeDestroyed, this, &InputPanelV1Window::destroyWindow); 0041 0042 connect(workspace(), &Workspace::outputsChanged, this, &InputPanelV1Window::reposition); 0043 0044 kwinApp()->inputMethod()->setPanel(this); 0045 } 0046 0047 void InputPanelV1Window::showOverlayPanel() 0048 { 0049 m_mode = Mode::Overlay; 0050 maybeShow(); 0051 } 0052 0053 void InputPanelV1Window::showTopLevel(OutputInterface *output, InputPanelSurfaceV1Interface::Position position) 0054 { 0055 m_mode = Mode::VirtualKeyboard; 0056 maybeShow(); 0057 } 0058 0059 void InputPanelV1Window::allow() 0060 { 0061 m_allowed = true; 0062 maybeShow(); 0063 } 0064 0065 void InputPanelV1Window::show() 0066 { 0067 m_virtualKeyboardShouldBeShown = true; 0068 maybeShow(); 0069 } 0070 0071 void InputPanelV1Window::hide() 0072 { 0073 m_virtualKeyboardShouldBeShown = false; 0074 if (readyForPainting() && m_mode != Mode::Overlay) { 0075 setHidden(true); 0076 } 0077 } 0078 0079 void InputPanelV1Window::reposition() 0080 { 0081 if (!readyForPainting()) { 0082 return; 0083 } 0084 0085 switch (m_mode) { 0086 case Mode::None: { 0087 // should never happen 0088 }; break; 0089 case Mode::VirtualKeyboard: { 0090 // maliit creates a fullscreen overlay so use the input shape as the window geometry. 0091 m_windowGeometry = surface()->input().boundingRect(); 0092 0093 const auto activeOutput = workspace()->activeOutput(); 0094 QRectF availableArea; 0095 if (waylandServer()->isScreenLocked()) { 0096 availableArea = workspace()->clientArea(FullScreenArea, this, activeOutput); 0097 } else { 0098 availableArea = workspace()->clientArea(MaximizeArea, this, activeOutput); 0099 } 0100 0101 QRectF geo = m_windowGeometry; 0102 geo.moveLeft(availableArea.left() + (availableArea.width() - geo.width()) / 2); 0103 geo.moveBottom(availableArea.bottom()); 0104 0105 moveResize(geo); 0106 } break; 0107 case Mode::Overlay: { 0108 auto textInputSurface = waylandServer()->seat()->focusedTextInputSurface(); 0109 auto textWindow = waylandServer()->findWindow(textInputSurface); 0110 QRect cursorRectangle; 0111 auto textInputV1 = waylandServer()->seat()->textInputV1(); 0112 if (textInputV1 && textInputV1->isEnabled() && textInputV1->surface() == textInputSurface) { 0113 cursorRectangle = textInputV1->cursorRectangle(); 0114 } 0115 auto textInputV2 = waylandServer()->seat()->textInputV2(); 0116 if (textInputV2 && textInputV2->isEnabled() && textInputV2->surface() == textInputSurface) { 0117 cursorRectangle = textInputV2->cursorRectangle(); 0118 } 0119 auto textInputV3 = waylandServer()->seat()->textInputV3(); 0120 if (textInputV3 && textInputV3->isEnabled() && textInputV3->surface() == textInputSurface) { 0121 cursorRectangle = textInputV3->cursorRectangle(); 0122 } 0123 if (textWindow) { 0124 cursorRectangle.translate(textWindow->bufferGeometry().topLeft().toPoint()); 0125 const QRectF screen = Workspace::self()->clientArea(PlacementArea, this, cursorRectangle.bottomLeft()); 0126 0127 m_windowGeometry = QRectF(QPointF(0, 0), surface()->size()); 0128 0129 // Reuse the similar logic like xdg popup 0130 QRectF popupRect(popupOffset(cursorRectangle, Qt::BottomEdge | Qt::LeftEdge, Qt::RightEdge | Qt::BottomEdge, m_windowGeometry.size()), m_windowGeometry.size()); 0131 0132 if (popupRect.left() < screen.left()) { 0133 popupRect.moveLeft(screen.left()); 0134 } 0135 if (popupRect.right() > screen.right()) { 0136 popupRect.moveRight(screen.right()); 0137 } 0138 if (popupRect.top() < screen.top() || popupRect.bottom() > screen.bottom()) { 0139 auto flippedPopupRect = 0140 QRectF(popupOffset(cursorRectangle, Qt::TopEdge | Qt::LeftEdge, Qt::RightEdge | Qt::TopEdge, m_windowGeometry.size()), m_windowGeometry.size()); 0141 0142 // if it still doesn't fit we should continue with the unflipped version 0143 if (flippedPopupRect.top() >= screen.top() && flippedPopupRect.bottom() <= screen.bottom()) { 0144 popupRect.moveTop(flippedPopupRect.top()); 0145 } 0146 } 0147 if (popupRect.top() < screen.top()) { 0148 popupRect.moveTop(screen.top()); 0149 } 0150 if (popupRect.bottom() > screen.bottom()) { 0151 popupRect.moveBottom(screen.bottom()); 0152 } 0153 0154 moveResize(popupRect); 0155 } 0156 } break; 0157 } 0158 } 0159 0160 void InputPanelV1Window::destroyWindow() 0161 { 0162 m_panelSurface->disconnect(this); 0163 m_panelSurface->surface()->disconnect(this); 0164 0165 markAsDeleted(); 0166 0167 Q_EMIT closed(); 0168 StackingUpdatesBlocker blocker(workspace()); 0169 waylandServer()->removeWindow(this); 0170 0171 unref(); 0172 } 0173 0174 NET::WindowType InputPanelV1Window::windowType() const 0175 { 0176 return NET::Utility; 0177 } 0178 0179 QRectF InputPanelV1Window::frameRectToBufferRect(const QRectF &rect) const 0180 { 0181 return QRectF(rect.topLeft() - m_windowGeometry.topLeft(), surface()->size()); 0182 } 0183 0184 void InputPanelV1Window::moveResizeInternal(const QRectF &rect, MoveResizeMode mode) 0185 { 0186 updateGeometry(rect); 0187 } 0188 0189 void InputPanelV1Window::handleMapped() 0190 { 0191 maybeShow(); 0192 } 0193 0194 void InputPanelV1Window::maybeShow() 0195 { 0196 const bool shouldShow = m_mode == Mode::Overlay || (m_mode == Mode::VirtualKeyboard && m_allowed && m_virtualKeyboardShouldBeShown); 0197 if (shouldShow && !isDeleted() && surface()->isMapped()) { 0198 markAsMapped(); 0199 reposition(); 0200 setHidden(false); 0201 } 0202 } 0203 0204 } // namespace KWin 0205 0206 #include "moc_inputpanelv1window.cpp"