2
* Copyright (C) 2014-2016 Canonical, Ltd.
4
* This program is free software: you can redistribute it and/or modify it under
5
* the terms of the GNU Lesser General Public License version 3, as published by
6
* the Free Software Foundation.
8
* This program is distributed in the hope that it will be useful, but WITHOUT
9
* ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10
* SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
* Lesser General Public License for more details.
13
* You should have received a copy of the GNU Lesser General Public License
14
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17
#include "surfaceobserver.h"
19
#include "namedcursor.h"
23
#include <QMetaObject>
24
#include <QMutableMapIterator>
25
#include <QMutexLocker>
28
#include <mir/geometry/size.h>
29
#include <mir/shell/surface_specification.h>
34
QRect calculateBoundingRect(const std::vector<mir::geometry::Rectangle> &rectVector)
37
for (auto mirRect : rectVector) {
38
boundingRect |= QRect(mirRect.top_left.x.as_int(),
39
mirRect.top_left.y.as_int(),
40
mirRect.size.width.as_int(),
41
mirRect.size.height.as_int());
46
} // anonymous namespace
48
QHash<const mir::scene::Surface*, SurfaceObserver*> SurfaceObserver::m_surfaceToObserverMap;
49
QMutex SurfaceObserver::mutex;
51
SurfaceObserver::SurfaceObserver()
53
, m_framesPosted(false)
55
m_cursorNameToShape["left_ptr"] = Qt::ArrowCursor;
56
m_cursorNameToShape["up_arrow"] = Qt::UpArrowCursor;
57
m_cursorNameToShape["cross"] = Qt::CrossCursor;
58
m_cursorNameToShape["watch"] = Qt::WaitCursor;
59
m_cursorNameToShape["xterm"] = Qt::IBeamCursor;
60
m_cursorNameToShape["size_ver"] = Qt::SizeVerCursor;
61
m_cursorNameToShape["size_hor"] = Qt::SizeHorCursor;
62
m_cursorNameToShape["size_bdiag"] = Qt::SizeBDiagCursor;
63
m_cursorNameToShape["size_fdiag"] = Qt::SizeFDiagCursor;
64
m_cursorNameToShape["size_all"] = Qt::SizeAllCursor;
65
m_cursorNameToShape["blank"] = Qt::BlankCursor;
66
m_cursorNameToShape["split_v"] = Qt::SplitVCursor;
67
m_cursorNameToShape["split_h"] = Qt::SplitHCursor;
68
m_cursorNameToShape["hand"] = Qt::PointingHandCursor;
69
m_cursorNameToShape["forbidden"] = Qt::ForbiddenCursor;
70
m_cursorNameToShape["whats_this"] = Qt::WhatsThisCursor;
71
m_cursorNameToShape["left_ptr_watch"] = Qt::BusyCursor;
72
m_cursorNameToShape["openhand"] = Qt::OpenHandCursor;
73
m_cursorNameToShape["closedhand"] = Qt::ClosedHandCursor;
74
m_cursorNameToShape["dnd-copy"] = Qt::DragCopyCursor;
75
m_cursorNameToShape["dnd-move"] = Qt::DragMoveCursor;
76
m_cursorNameToShape["dnd-link"] = Qt::DragLinkCursor;
78
// Used by Mir client API (mir_*_cursor_name strings)
79
m_cursorNameToShape["default"] = Qt::ArrowCursor;
80
m_cursorNameToShape["disabled"] = Qt::BlankCursor;
81
m_cursorNameToShape["arrow"] = Qt::ArrowCursor;
82
m_cursorNameToShape["busy"] = Qt::WaitCursor;
83
m_cursorNameToShape["caret"] = Qt::IBeamCursor;
84
m_cursorNameToShape["pointing-hand"] = Qt::PointingHandCursor;
85
m_cursorNameToShape["open-hand"] = Qt::OpenHandCursor;
86
m_cursorNameToShape["closed-hand"] = Qt::ClosedHandCursor;
87
m_cursorNameToShape["horizontal-resize"] = Qt::SizeHorCursor;
88
m_cursorNameToShape["vertical-resize"] = Qt::SizeVerCursor;
89
m_cursorNameToShape["diagonal-resize-bottom-to-top"] = Qt::SizeBDiagCursor;
90
m_cursorNameToShape["diagonal-resize-top_to_bottom"] = Qt::SizeFDiagCursor; // current string with typo
91
m_cursorNameToShape["diagonal-resize-top-to-bottom"] = Qt::SizeFDiagCursor; // how it will be when they fix it (if ever)
92
m_cursorNameToShape["omnidirectional-resize"] = Qt::SizeAllCursor;
93
m_cursorNameToShape["vsplit-resize"] = Qt::SplitVCursor;
94
m_cursorNameToShape["hsplit-resize"] = Qt::SplitHCursor;
95
m_cursorNameToShape["crosshair"] = Qt::CrossCursor;
97
qRegisterMetaType<MirShellChrome>("MirShellChrome");
100
SurfaceObserver::~SurfaceObserver()
102
QMutexLocker locker(&mutex);
103
QMutableHashIterator<const mir::scene::Surface*, SurfaceObserver*> i(m_surfaceToObserverMap);
104
while (i.hasNext()) {
106
if (i.value() == this) {
113
void SurfaceObserver::setListener(QObject *listener)
115
m_listener = listener;
116
if (m_framesPosted) {
117
Q_EMIT framesPosted();
121
void SurfaceObserver::frame_posted(int /*frames_available*/, mir::geometry::Size const& /*size*/)
123
m_framesPosted = true;
125
Q_EMIT framesPosted();
129
void SurfaceObserver::renamed(char const * name)
131
Q_EMIT nameChanged(QString::fromUtf8(name));
134
void SurfaceObserver::cursor_image_removed()
136
Q_EMIT cursorChanged(QCursor());
139
void SurfaceObserver::attrib_changed(MirSurfaceAttrib attribute, int value)
142
Q_EMIT attributeChanged(attribute, value);
146
void SurfaceObserver::resized_to(mir::geometry::Size const&size)
148
Q_EMIT resized(QSize(size.width.as_int(), size.height.as_int()));
151
void SurfaceObserver::notifySurfaceModifications(const mir::shell::SurfaceSpecification &modifications)
153
if (modifications.min_width.is_set()) {
154
Q_EMIT minimumWidthChanged(modifications.min_width.value().as_int());
156
if (modifications.min_height.is_set()) {
157
Q_EMIT minimumHeightChanged(modifications.min_height.value().as_int());
159
if (modifications.max_width.is_set()) {
160
Q_EMIT maximumWidthChanged(modifications.max_width.value().as_int());
162
if (modifications.max_height.is_set()) {
163
Q_EMIT maximumHeightChanged(modifications.max_height.value().as_int());
165
if (modifications.width_inc.is_set()) {
166
Q_EMIT widthIncrementChanged(modifications.width_inc.value().as_int());
168
if (modifications.height_inc.is_set()) {
169
Q_EMIT heightIncrementChanged(modifications.height_inc.value().as_int());
171
if (modifications.shell_chrome.is_set()) {
172
Q_EMIT shellChromeChanged(modifications.shell_chrome.value());
174
if (modifications.input_shape.is_set()) {
175
QRect qRect = calculateBoundingRect(modifications.input_shape.value());
176
Q_EMIT inputBoundsChanged(qRect);
180
SurfaceObserver *SurfaceObserver::observerForSurface(const mir::scene::Surface *surface)
182
if (m_surfaceToObserverMap.contains(surface)) {
183
return m_surfaceToObserverMap.value(surface);
189
void SurfaceObserver::registerObserverForSurface(SurfaceObserver *observer, const mir::scene::Surface *surface)
191
QMutexLocker locker(&mutex);
192
m_surfaceToObserverMap[surface] = observer;
195
void SurfaceObserver::cursor_image_set_to(const mir::graphics::CursorImage &cursorImage)
197
QCursor qcursor = createQCursorFromMirCursorImage(cursorImage);
198
Q_EMIT cursorChanged(qcursor);
201
void SurfaceObserver::keymap_changed(MirInputDeviceId, const std::string &, const std::string &layout,
202
const std::string &variant, const std::string &)
204
Q_EMIT keymapChanged(QString::fromStdString(layout), QString::fromStdString(variant));
207
QCursor SurfaceObserver::createQCursorFromMirCursorImage(const mir::graphics::CursorImage &cursorImage) {
208
if (cursorImage.as_argb_8888() == nullptr) {
209
// Must be a named cursor
210
auto namedCursor = dynamic_cast<const qtmir::NamedCursor*>(&cursorImage);
211
Q_ASSERT(namedCursor != nullptr);
213
// NB: If we need a named cursor not covered by Qt::CursorShape, we won't be able to
214
// used Qt's cursor API anymore for transmitting MirSurface's cursor image.
216
Qt::CursorShape cursorShape = Qt::ArrowCursor;
218
auto iterator = m_cursorNameToShape.constFind(namedCursor->name());
219
if (iterator == m_cursorNameToShape.constEnd()) {
220
qCWarning(QTMIR_SURFACES).nospace() << "SurfaceObserver: unrecognized cursor name "
221
<< namedCursor->name();
223
cursorShape = iterator.value();
226
return QCursor(cursorShape);
232
QImage image((const uchar*)cursorImage.as_argb_8888(),
233
cursorImage.size().width.as_int(), cursorImage.size().height.as_int(), QImage::Format_ARGB32);
235
return QCursor(QPixmap::fromImage(image), cursorImage.hotspot().dx.as_int(), cursorImage.hotspot().dy.as_int());