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 examples of the Qt Wayland module
7**
8** $QT_BEGIN_LICENSE:BSD$
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** BSD License Usage
18** Alternatively, you may use this file under the terms of the BSD license
19** as follows:
20**
21** "Redistribution and use in source and binary forms, with or without
22** modification, are permitted provided that the following conditions are
23** met:
24** * Redistributions of source code must retain the above copyright
25** notice, this list of conditions and the following disclaimer.
26** * Redistributions in binary form must reproduce the above copyright
27** notice, this list of conditions and the following disclaimer in
28** the documentation and/or other materials provided with the
29** distribution.
30** * Neither the name of The Qt Company Ltd nor the names of its
31** contributors may be used to endorse or promote products derived
32** from this software without specific prior written permission.
33**
34**
35** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46**
47** $QT_END_LICENSE$
48**
49****************************************************************************/
50
51#include "compositor.h"
52#include "window.h"
53
54#include <QtWaylandCompositor/QWaylandOutput>
55#include <QtWaylandCompositor/QWaylandIviApplication>
56#include <QtWaylandCompositor/QWaylandIviSurface>
57#include <QtWaylandCompositor/QWaylandSeat>
58
59#include <QRandomGenerator>
60#include <QOpenGLFunctions>
61
62QOpenGLTexture *View::getTexture() {
63 if (advance())
64 m_texture = currentBuffer().toOpenGLTexture();
65 return m_texture;
66}
67
68QPoint View::mapToLocal(const QPoint &globalPos) const
69{
70 return globalPos - globalPosition();
71}
72
73
74// Normally, an IVI based compositor would have a design where each window has
75// a defined position, based on the id. In this example, we just assign a random position.
76
77void View::initPosition(const QSize &screenSize, const QSize &surfaceSize)
78{
79 if (m_positionSet)
80 return;
81 QRandomGenerator rand(iviId());
82 int xrange = qMax(a: screenSize.width() - surfaceSize.width(), b: 1);
83 int yrange = qMax(a: screenSize.height() - surfaceSize.height(), b: 1);
84 setGlobalPosition(QPoint(rand.bounded(highest: xrange), rand.bounded(highest: yrange)));
85}
86
87Compositor::Compositor(Window *window)
88 : m_window(window)
89{
90 window->setCompositor(this);
91 connect(sender: window, signal: &Window::glReady, context: this, slot: [this] { create(); });
92}
93
94Compositor::~Compositor()
95{
96}
97
98void Compositor::create()
99{
100 QWaylandOutput *output = new QWaylandOutput(this, m_window);
101 QWaylandOutputMode mode(m_window->size(), 60000);
102 output->addMode(mode, preferred: true);
103 QWaylandCompositor::create();
104 output->setCurrentMode(mode);
105
106 m_iviApplication = new QWaylandIviApplication(this);
107 connect(sender: m_iviApplication, signal: &QWaylandIviApplication::iviSurfaceCreated, receiver: this, slot: &Compositor::onIviSurfaceCreated);
108}
109
110View *Compositor::viewAt(const QPoint &position)
111{
112 // Since views are stored in painting order (back to front), we have to iterate backwards
113 // to find the topmost view at a given point.
114 for (auto it = m_views.crbegin(); it != m_views.crend(); ++it) {
115 View *view = *it;
116 if (view->globalGeometry().contains(p: position))
117 return view;
118 }
119 return nullptr;
120}
121
122void Compositor::raise(View *view)
123{
124 m_views.removeAll(t: view);
125 m_views.append(t: view);
126 defaultSeat()->setKeyboardFocus(view->surface());
127 triggerRender();
128}
129
130static inline QPoint mapToView(const View *view, const QPoint &position)
131{
132 return view ? view->mapToLocal(globalPos: position) : position;
133}
134
135void Compositor::handleMousePress(const QPoint &position, Qt::MouseButton button)
136{
137 if (!m_mouseView) {
138 if (m_mouseView = viewAt(position))
139 raise(view: m_mouseView);
140 }
141 auto *seat = defaultSeat();
142 seat->sendMouseMoveEvent(surface: m_mouseView, localPos: mapToView(view: m_mouseView, position));
143 seat->sendMousePressEvent(button);
144}
145
146void Compositor::handleMouseRelease(const QPoint &position, Qt::MouseButton button, Qt::MouseButtons buttons)
147{
148 auto *seat = defaultSeat();
149 seat->sendMouseMoveEvent(surface: m_mouseView, localPos: mapToView(view: m_mouseView, position));
150 seat->sendMouseReleaseEvent(button);
151
152 if (buttons == Qt::NoButton) {
153 View *newView = viewAt(position);
154 if (newView != m_mouseView)
155 seat->sendMouseMoveEvent(surface: newView, localPos: mapToView(view: newView, position));
156 m_mouseView = nullptr;
157 }
158}
159
160void Compositor::handleMouseMove(const QPoint &position)
161{
162 View *view = m_mouseView ? m_mouseView.data() : viewAt(position);
163 defaultSeat()->sendMouseMoveEvent(surface: view, localPos: mapToView(view, position));
164}
165
166void Compositor::handleMouseWheel(const QPoint &angleDelta)
167{
168 // TODO: fix this to send a single event, when diagonal scrolling is supported
169 if (angleDelta.x() != 0)
170 defaultSeat()->sendMouseWheelEvent(orientation: Qt::Horizontal, delta: angleDelta.x());
171 if (angleDelta.y() != 0)
172 defaultSeat()->sendMouseWheelEvent(orientation: Qt::Vertical, delta: angleDelta.y());
173}
174
175void Compositor::handleKeyPress(quint32 nativeScanCode)
176{
177 defaultSeat()->sendKeyPressEvent(code: nativeScanCode);
178}
179
180void Compositor::handleKeyRelease(quint32 nativeScanCode)
181{
182 defaultSeat()->sendKeyReleaseEvent(code: nativeScanCode);
183}
184
185
186void Compositor::onIviSurfaceCreated(QWaylandIviSurface *iviSurface)
187{
188 View *view = new View(iviSurface->iviId());
189 view->setSurface(iviSurface->surface());
190 view->setOutput(outputFor(window: m_window));
191
192 m_views << view;
193 connect(sender: view, signal: &QWaylandView::surfaceDestroyed, receiver: this, slot: &Compositor::viewSurfaceDestroyed);
194 connect(sender: iviSurface->surface(), signal: &QWaylandSurface::redraw, receiver: this, slot: &Compositor::triggerRender);
195}
196
197void Compositor::onSurfaceDestroyed()
198{
199 triggerRender();
200}
201
202void Compositor::viewSurfaceDestroyed()
203{
204 View *view = qobject_cast<View*>(object: sender());
205 m_views.removeAll(t: view);
206 delete view;
207 triggerRender();
208}
209
210void Compositor::triggerRender()
211{
212 m_window->requestUpdate();
213}
214
215void Compositor::startRender()
216{
217 QWaylandOutput *out = defaultOutput();
218 if (out)
219 out->frameStarted();
220}
221
222void Compositor::endRender()
223{
224 QWaylandOutput *out = defaultOutput();
225 if (out)
226 out->sendFrameCallbacks();
227}
228

source code of qtwayland/examples/wayland/minimal-cpp/compositor.cpp