File indexing completed on 2024-10-06 13:22:07

0001 /*
0002     SPDX-FileCopyrightText: 2017 Martin Graesslin <mgraesslin@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005 
0006 */
0007 #include "popup_input_filter.h"
0008 #include "deleted.h"
0009 #include "input_event.h"
0010 #include "internalwindow.h"
0011 #include "wayland/seat_interface.h"
0012 #include "wayland_server.h"
0013 #include "window.h"
0014 #include "workspace.h"
0015 
0016 namespace KWin
0017 {
0018 
0019 PopupInputFilter::PopupInputFilter()
0020     : QObject()
0021 {
0022     connect(workspace(), &Workspace::windowAdded, this, &PopupInputFilter::handleWindowAdded);
0023     connect(workspace(), &Workspace::internalWindowAdded, this, &PopupInputFilter::handleWindowAdded);
0024 }
0025 
0026 void PopupInputFilter::handleWindowAdded(Window *window)
0027 {
0028     if (m_popupWindows.contains(window)) {
0029         return;
0030     }
0031     if (window->hasPopupGrab()) {
0032         // TODO: verify that the Window is allowed as a popup
0033         connect(window, &Window::windowShown, this, &PopupInputFilter::handleWindowAdded, Qt::UniqueConnection);
0034         connect(window, &Window::windowClosed, this, &PopupInputFilter::handleWindowRemoved, Qt::UniqueConnection);
0035         m_popupWindows << window;
0036     }
0037 }
0038 
0039 void PopupInputFilter::handleWindowRemoved(Window *window)
0040 {
0041     m_popupWindows.removeOne(window);
0042 }
0043 
0044 bool PopupInputFilter::pointerEvent(MouseEvent *event, quint32 nativeButton)
0045 {
0046     if (m_popupWindows.isEmpty()) {
0047         return false;
0048     }
0049     if (event->type() == QMouseEvent::MouseButtonPress) {
0050         auto pointerFocus = input()->findToplevel(event->globalPos());
0051         if (!pointerFocus || !Window::belongToSameApplication(pointerFocus, m_popupWindows.constLast())) {
0052             // a press on a window (or no window) not belonging to the popup window
0053             cancelPopups();
0054             // filter out this press
0055             return true;
0056         }
0057         if (pointerFocus && pointerFocus->isDecorated()) {
0058             // test whether it is on the decoration
0059             if (!pointerFocus->clientGeometry().contains(event->globalPos())) {
0060                 cancelPopups();
0061                 return true;
0062             }
0063         }
0064     }
0065     return false;
0066 }
0067 
0068 bool PopupInputFilter::keyEvent(KeyEvent *event)
0069 {
0070     if (m_popupWindows.isEmpty()) {
0071         return false;
0072     }
0073 
0074     auto seat = waylandServer()->seat();
0075 
0076     auto last = m_popupWindows.last();
0077     if (last->surface() == nullptr) {
0078         return false;
0079     }
0080 
0081     seat->setFocusedKeyboardSurface(last->surface());
0082 
0083     if (!passToInputMethod(event)) {
0084         passToWaylandServer(event);
0085     }
0086 
0087     return true;
0088 }
0089 
0090 bool PopupInputFilter::touchDown(qint32 id, const QPointF &pos, std::chrono::microseconds time)
0091 {
0092     if (m_popupWindows.isEmpty()) {
0093         return false;
0094     }
0095     auto pointerFocus = input()->findToplevel(pos);
0096     if (!pointerFocus || !Window::belongToSameApplication(pointerFocus, m_popupWindows.constLast())) {
0097         // a touch on a window (or no window) not belonging to the popup window
0098         cancelPopups();
0099         // filter out this touch
0100         return true;
0101     }
0102     if (pointerFocus && pointerFocus->isDecorated()) {
0103         // test whether it is on the decoration
0104         if (!pointerFocus->clientGeometry().contains(pos)) {
0105             cancelPopups();
0106             return true;
0107         }
0108     }
0109     return false;
0110 }
0111 
0112 void PopupInputFilter::cancelPopups()
0113 {
0114     while (!m_popupWindows.isEmpty()) {
0115         auto c = m_popupWindows.takeLast();
0116         c->popupDone();
0117     }
0118 }
0119 
0120 }