1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2017 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtWaylandCompositor module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 or (at your option) any later version |
20 | ** approved by the KDE Free Qt Foundation. The licenses are as published by |
21 | ** the Free Software Foundation and appearing in the file LICENSE.GPL3 |
22 | ** included in the packaging of this file. Please review the following |
23 | ** information to ensure the GNU General Public License requirements will |
24 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
25 | ** |
26 | ** $QT_END_LICENSE$ |
27 | ** |
28 | ****************************************************************************/ |
29 | |
30 | #include "qwaylandxdgshellv5integration_p.h" |
31 | |
32 | #include <QtWaylandCompositor/QWaylandQuickShellSurfaceItem> |
33 | #include <QtWaylandCompositor/QWaylandCompositor> |
34 | #include <QtWaylandCompositor/QWaylandSeat> |
35 | #include <QtWaylandCompositor/private/qwaylandxdgshellv5_p.h> |
36 | #include <QMouseEvent> |
37 | #include <QGuiApplication> |
38 | |
39 | QT_BEGIN_NAMESPACE |
40 | |
41 | #if QT_DEPRECATED_SINCE(5, 15) |
42 | |
43 | namespace QtWayland { |
44 | |
45 | static void handlePopupCreated(QWaylandQuickShellSurfaceItem *parentItem, QWaylandXdgPopupV5 *) |
46 | { |
47 | if (parentItem->surface() == popup->parentSurface()) |
48 | QWaylandQuickShellSurfaceItemPrivate::get(item: parentItem)->maybeCreateAutoPopup(shellSurface: popup); |
49 | } |
50 | |
51 | XdgShellV5Integration::XdgShellV5Integration(QWaylandQuickShellSurfaceItem *item) |
52 | : QWaylandQuickShellIntegration(item) |
53 | , m_item(item) |
54 | , m_xdgSurface(qobject_cast<QWaylandXdgSurfaceV5 *>(object: item->shellSurface())) |
55 | , grabberState(GrabberState::Default) |
56 | { |
57 | m_item->setSurface(m_xdgSurface->surface()); |
58 | connect(sender: m_xdgSurface, signal: &QWaylandXdgSurfaceV5::startMove, receiver: this, slot: &XdgShellV5Integration::handleStartMove); |
59 | connect(sender: m_xdgSurface, signal: &QWaylandXdgSurfaceV5::startResize, receiver: this, slot: &XdgShellV5Integration::handleStartResize); |
60 | connect(sender: m_xdgSurface, signal: &QWaylandXdgSurfaceV5::setTopLevel, receiver: this, slot: &XdgShellV5Integration::handleSetTopLevel); |
61 | connect(sender: m_xdgSurface, signal: &QWaylandXdgSurfaceV5::setTransient, receiver: this, slot: &XdgShellV5Integration::handleSetTransient); |
62 | connect(sender: m_xdgSurface, signal: &QWaylandXdgSurfaceV5::setMaximized, receiver: this, slot: &XdgShellV5Integration::handleSetMaximized); |
63 | connect(sender: m_xdgSurface, signal: &QWaylandXdgSurfaceV5::unsetMaximized, receiver: this, slot: &XdgShellV5Integration::handleUnsetMaximized); |
64 | connect(sender: m_xdgSurface, signal: &QWaylandXdgSurfaceV5::maximizedChanged, receiver: this, slot: &XdgShellV5Integration::handleMaximizedChanged); |
65 | connect(sender: m_xdgSurface, signal: &QWaylandXdgSurfaceV5::activatedChanged, receiver: this, slot: &XdgShellV5Integration::handleActivatedChanged); |
66 | connect(sender: m_xdgSurface->surface(), signal: &QWaylandSurface::destinationSizeChanged, receiver: this, slot: &XdgShellV5Integration::handleSurfaceSizeChanged); |
67 | connect(sender: m_xdgSurface->shell(), signal: &QWaylandXdgShellV5::xdgPopupCreated, context: this, slot: [item](QWaylandXdgPopupV5 *){ |
68 | handlePopupCreated(parentItem: item, popup); |
69 | }); |
70 | } |
71 | |
72 | XdgShellV5Integration::~XdgShellV5Integration() |
73 | { |
74 | m_item->setSurface(nullptr); |
75 | } |
76 | |
77 | bool XdgShellV5Integration::eventFilter(QObject *object, QEvent *event) |
78 | { |
79 | if (event->type() == QEvent::MouseMove) { |
80 | QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event); |
81 | return filterMouseMoveEvent(event: mouseEvent); |
82 | } else if (event->type() == QEvent::MouseButtonRelease) { |
83 | QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event); |
84 | return filterMouseReleaseEvent(event: mouseEvent); |
85 | } |
86 | return QWaylandQuickShellIntegration::eventFilter(watched: object, event); |
87 | } |
88 | |
89 | bool XdgShellV5Integration::filterMouseMoveEvent(QMouseEvent *event) |
90 | { |
91 | if (grabberState == GrabberState::Resize) { |
92 | Q_ASSERT(resizeState.seat == m_item->compositor()->seatFor(event)); |
93 | if (!resizeState.initialized) { |
94 | resizeState.initialMousePos = event->windowPos(); |
95 | resizeState.initialized = true; |
96 | return true; |
97 | } |
98 | QPointF delta = m_item->mapToSurface(point: event->windowPos() - resizeState.initialMousePos); |
99 | QSize newSize = m_xdgSurface->sizeForResize(size: resizeState.initialWindowSize, delta, edge: resizeState.resizeEdges); |
100 | m_xdgSurface->sendResizing(maxSize: newSize); |
101 | } else if (grabberState == GrabberState::Move) { |
102 | Q_ASSERT(moveState.seat == m_item->compositor()->seatFor(event)); |
103 | QQuickItem *moveItem = m_item->moveItem(); |
104 | if (!moveState.initialized) { |
105 | moveState.initialOffset = moveItem->mapFromItem(item: nullptr, point: event->windowPos()); |
106 | moveState.initialized = true; |
107 | return true; |
108 | } |
109 | if (!moveItem->parentItem()) |
110 | return true; |
111 | QPointF parentPos = moveItem->parentItem()->mapFromItem(item: nullptr, point: event->windowPos()); |
112 | moveItem->setPosition(parentPos - moveState.initialOffset); |
113 | } |
114 | return false; |
115 | } |
116 | |
117 | bool XdgShellV5Integration::filterMouseReleaseEvent(QMouseEvent *event) |
118 | { |
119 | Q_UNUSED(event); |
120 | |
121 | if (grabberState == GrabberState::Resize) { |
122 | m_xdgSurface->sendUnmaximized(); |
123 | grabberState = GrabberState::Default; |
124 | return true; |
125 | } else if (grabberState == GrabberState::Move) { |
126 | grabberState = GrabberState::Default; |
127 | return true; |
128 | } |
129 | return false; |
130 | } |
131 | |
132 | void XdgShellV5Integration::handleStartMove(QWaylandSeat *seat) |
133 | { |
134 | grabberState = GrabberState::Move; |
135 | moveState.seat = seat; |
136 | moveState.initialized = false; |
137 | } |
138 | |
139 | void XdgShellV5Integration::handleStartResize(QWaylandSeat *seat, QWaylandXdgSurfaceV5::ResizeEdge edges) |
140 | { |
141 | grabberState = GrabberState::Resize; |
142 | resizeState.seat = seat; |
143 | resizeState.resizeEdges = edges; |
144 | resizeState.initialWindowSize = m_xdgSurface->windowGeometry().size(); |
145 | resizeState.initialPosition = m_item->moveItem()->position(); |
146 | resizeState.initialSurfaceSize = m_item->surface()->destinationSize(); |
147 | resizeState.initialized = false; |
148 | } |
149 | |
150 | void XdgShellV5Integration::handleSetTopLevel() |
151 | { |
152 | if (m_xdgSurface->shell()->focusPolicy() == QWaylandShell::AutomaticFocus) |
153 | m_item->takeFocus(); |
154 | } |
155 | |
156 | void XdgShellV5Integration::handleSetTransient() |
157 | { |
158 | if (m_xdgSurface->shell()->focusPolicy() == QWaylandShell::AutomaticFocus) |
159 | m_item->takeFocus(); |
160 | } |
161 | |
162 | void XdgShellV5Integration::handleSetMaximized() |
163 | { |
164 | if (!m_item->view()->isPrimary()) |
165 | return; |
166 | |
167 | maximizeState.initialWindowSize = m_xdgSurface->windowGeometry().size(); |
168 | maximizeState.initialPosition = m_item->moveItem()->position(); |
169 | |
170 | QWaylandOutput *output = m_item->view()->output(); |
171 | m_xdgSurface->sendMaximized(size: output->availableGeometry().size() / output->scaleFactor()); |
172 | } |
173 | |
174 | void XdgShellV5Integration::handleUnsetMaximized() |
175 | { |
176 | if (!m_item->view()->isPrimary()) |
177 | return; |
178 | |
179 | m_xdgSurface->sendUnmaximized(size: maximizeState.initialWindowSize); |
180 | } |
181 | |
182 | void XdgShellV5Integration::handleMaximizedChanged() |
183 | { |
184 | if (m_xdgSurface->maximized()) { |
185 | QWaylandOutput *output = m_item->view()->output(); |
186 | m_item->moveItem()->setPosition(output->position() + output->availableGeometry().topLeft()); |
187 | } else { |
188 | m_item->moveItem()->setPosition(maximizeState.initialPosition); |
189 | } |
190 | } |
191 | |
192 | void XdgShellV5Integration::handleActivatedChanged() |
193 | { |
194 | if (m_xdgSurface->activated()) |
195 | m_item->raise(); |
196 | } |
197 | |
198 | void XdgShellV5Integration::handleSurfaceSizeChanged() |
199 | { |
200 | if (grabberState == GrabberState::Resize) { |
201 | qreal dx = 0; |
202 | qreal dy = 0; |
203 | if (resizeState.resizeEdges & QWaylandXdgSurfaceV5::ResizeEdge::TopEdge) |
204 | dy = resizeState.initialSurfaceSize.height() - m_item->surface()->destinationSize().height(); |
205 | if (resizeState.resizeEdges & QWaylandXdgSurfaceV5::ResizeEdge::LeftEdge) |
206 | dx = resizeState.initialSurfaceSize.width() - m_item->surface()->destinationSize().width(); |
207 | QPointF offset = m_item->mapFromSurface(point: {dx, dy}); |
208 | m_item->moveItem()->setPosition(resizeState.initialPosition + offset); |
209 | } |
210 | } |
211 | |
212 | XdgPopupV5Integration::XdgPopupV5Integration(QWaylandQuickShellSurfaceItem *item) |
213 | : QWaylandQuickShellIntegration (item) |
214 | , m_item(item) |
215 | , m_xdgPopup(qobject_cast<QWaylandXdgPopupV5 *>(object: item->shellSurface())) |
216 | , m_xdgShell(QWaylandXdgPopupV5Private::get(xdgPopup: m_xdgPopup)->m_xdgShell) |
217 | { |
218 | item->setSurface(m_xdgPopup->surface()); |
219 | if (item->view()->output()) { |
220 | QPoint position = item->mapFromSurface(point: m_xdgPopup->position()).toPoint(); |
221 | item->moveItem()->setPosition(position); |
222 | } else { |
223 | qWarning() << "XdgPopupV5Integration popup item without output" << item; |
224 | } |
225 | |
226 | QWaylandClient *client = m_xdgPopup->surface()->client(); |
227 | auto shell = m_xdgShell; |
228 | QWaylandQuickShellEventFilter::startFilter(client, closePopupCallback: [shell]() { shell->closeAllPopups(); }); |
229 | |
230 | connect(sender: m_xdgPopup, signal: &QWaylandXdgPopupV5::destroyed, receiver: this, slot: &XdgPopupV5Integration::handlePopupDestroyed); |
231 | connect(sender: m_xdgPopup->shell(), signal: &QWaylandXdgShellV5::xdgPopupCreated, context: this, slot: [item](QWaylandXdgPopupV5 *) { |
232 | handlePopupCreated(parentItem: item, popup); |
233 | }); |
234 | } |
235 | |
236 | XdgPopupV5Integration::~XdgPopupV5Integration() |
237 | { |
238 | m_item->setSurface(nullptr); |
239 | } |
240 | |
241 | void XdgPopupV5Integration::handlePopupDestroyed() |
242 | { |
243 | QWaylandXdgShellV5Private *shellPrivate = QWaylandXdgShellV5Private::get(xdgShell: m_xdgShell); |
244 | auto = shellPrivate->m_xdgPopups; |
245 | if (popups.isEmpty()) |
246 | QWaylandQuickShellEventFilter::cancelFilter(); |
247 | } |
248 | |
249 | } |
250 | |
251 | #endif // QT_DEPRECATED_SINCE(5, 15) |
252 | |
253 | QT_END_NAMESPACE |
254 | |