File indexing completed on 2024-11-10 04:57:54
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 "input_event.h" 0009 #include "internalwindow.h" 0010 #include "keyboard_input.h" 0011 #include "wayland/seat.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 } 0024 0025 void PopupInputFilter::handleWindowAdded(Window *window) 0026 { 0027 if (m_popupWindows.contains(window)) { 0028 return; 0029 } 0030 if (window->hasPopupGrab()) { 0031 // TODO: verify that the Window is allowed as a popup 0032 m_popupWindows << window; 0033 connect(window, &Window::closed, this, [this, window]() { 0034 m_popupWindows.removeOne(window); 0035 // Move focus to the parent popup. If that's the last popup, then move focus back to the parent 0036 if (!m_popupWindows.isEmpty() && m_popupWindows.last()->surface()) { 0037 auto seat = waylandServer()->seat(); 0038 seat->setFocusedKeyboardSurface(m_popupWindows.last()->surface()); 0039 } else { 0040 input()->keyboard()->update(); 0041 } 0042 }); 0043 } 0044 } 0045 0046 bool PopupInputFilter::pointerEvent(MouseEvent *event, quint32 nativeButton) 0047 { 0048 if (m_popupWindows.isEmpty()) { 0049 return false; 0050 } 0051 if (event->type() == QMouseEvent::MouseButtonPress) { 0052 auto pointerFocus = input()->findToplevel(event->globalPos()); 0053 if (!pointerFocus || !Window::belongToSameApplication(pointerFocus, m_popupWindows.constLast())) { 0054 // a press on a window (or no window) not belonging to the popup window 0055 cancelPopups(); 0056 // filter out this press 0057 return true; 0058 } 0059 if (pointerFocus && pointerFocus->isDecorated()) { 0060 // test whether it is on the decoration 0061 if (!exclusiveContains(pointerFocus->clientGeometry(), event->globalPos())) { 0062 cancelPopups(); 0063 return true; 0064 } 0065 } 0066 } 0067 return false; 0068 } 0069 0070 bool PopupInputFilter::keyEvent(KeyEvent *event) 0071 { 0072 if (m_popupWindows.isEmpty()) { 0073 return false; 0074 } 0075 0076 auto seat = waylandServer()->seat(); 0077 0078 auto last = m_popupWindows.last(); 0079 if (last->surface() == nullptr) { 0080 return false; 0081 } 0082 0083 seat->setFocusedKeyboardSurface(last->surface()); 0084 0085 if (!passToInputMethod(event)) { 0086 passToWaylandServer(event); 0087 } 0088 0089 return true; 0090 } 0091 0092 bool PopupInputFilter::touchDown(qint32 id, const QPointF &pos, std::chrono::microseconds time) 0093 { 0094 if (m_popupWindows.isEmpty()) { 0095 return false; 0096 } 0097 auto pointerFocus = input()->findToplevel(pos); 0098 if (!pointerFocus || !Window::belongToSameApplication(pointerFocus, m_popupWindows.constLast())) { 0099 // a touch on a window (or no window) not belonging to the popup window 0100 cancelPopups(); 0101 // filter out this touch 0102 return true; 0103 } 0104 if (pointerFocus && pointerFocus->isDecorated()) { 0105 // test whether it is on the decoration 0106 if (!exclusiveContains(pointerFocus->clientGeometry(), pos)) { 0107 cancelPopups(); 0108 return true; 0109 } 0110 } 0111 return false; 0112 } 0113 0114 void PopupInputFilter::cancelPopups() 0115 { 0116 while (!m_popupWindows.isEmpty()) { 0117 auto c = m_popupWindows.takeLast(); 0118 c->popupDone(); 0119 } 0120 } 0121 0122 } 0123 0124 #include "moc_popup_input_filter.cpp"