on xenial help sign in
merge prereq
c93aefaMichael Zanetti
debian/unity8.install+4
@@ -16,7 +16,11 @@ usr/share/unity8/Rotation
16usr/share/unity8/DeviceConfiguration.qml
16usr/share/unity8/DeviceConfiguration.qml
17usr/share/unity8/OrientedShell.qml
17usr/share/unity8/OrientedShell.qml
18usr/share/unity8/DisabledScreenNotice.qml
18usr/share/unity8/DisabledScreenNotice.qml
19+usr/share/unity8/qmldir
19usr/share/unity8/Shell.qml
20usr/share/unity8/Shell.qml
21+usr/share/unity8/ShellApplication.qml
22+usr/share/unity8/ShellNotifier.qml
23+usr/share/unity8/ShellScreen.qml
20usr/share/unity8/Stage
24usr/share/unity8/Stage
21usr/share/unity8/Tutorial
25usr/share/unity8/Tutorial
22usr/share/unity8/Wizard
26usr/share/unity8/Wizard
plugins/Cursor/InputDispatcherFilter.cpp+73−44
@@ -19,7 +19,9 @@
19
19
20#include <QEvent>
20#include <QEvent>
21#include <QGuiApplication>
21#include <QGuiApplication>
22+#include <QQuickWindow>
22#include <QScreen>
23#include <QScreen>
24+#include <QtMath>
23#include <qpa/qplatformnativeinterface.h>
25#include <qpa/qplatformnativeinterface.h>
24#include <qpa/qplatformscreen.h>
26#include <qpa/qplatformscreen.h>
25
27
@@ -31,6 +33,7 @@ InputDispatcherFilter *InputDispatcherFilter::instance()
31
33
32InputDispatcherFilter::InputDispatcherFilter(QObject *parent)
34InputDispatcherFilter::InputDispatcherFilter(QObject *parent)
33 : QObject(parent)
35 : QObject(parent)
36+ , m_pushing(false)
34{
37{
35 QPlatformNativeInterface *ni = QGuiApplication::platformNativeInterface();
38 QPlatformNativeInterface *ni = QGuiApplication::platformNativeInterface();
36 m_inputDispatcher = static_cast<QObject*>(ni->nativeResourceForIntegration("InputDispatcher"));
39 m_inputDispatcher = static_cast<QObject*>(ni->nativeResourceForIntegration("InputDispatcher"));
@@ -42,25 +45,12 @@ InputDispatcherFilter::InputDispatcherFilter(QObject *parent)
42void InputDispatcherFilter::registerPointer(MousePointer *pointer)
45void InputDispatcherFilter::registerPointer(MousePointer *pointer)
43{
46{
44 // allow first registered pointer to be visible.
47 // allow first registered pointer to be visible.
45− pointer->setVisible(m_pointers.count() == 0);
46−
47 m_pointers.insert(pointer);
48 m_pointers.insert(pointer);
48− connect(pointer, &MousePointer::mouseMoved, this, [this, pointer]() {
49− Q_FOREACH(auto p, m_pointers) {
50− p->setVisible(p == pointer);
51− }
52− });
53}
49}
54
50
55void InputDispatcherFilter::unregisterPointer(MousePointer *pointer)
51void InputDispatcherFilter::unregisterPointer(MousePointer *pointer)
56{
52{
57 m_pointers.remove(pointer);
53 m_pointers.remove(pointer);
58− disconnect(pointer, &MousePointer::mouseMoved, this, 0);
59−}
60−
61−void InputDispatcherFilter::setPosition(const QPointF &pos)
62−{
63− mousePosition = pos;
64}
54}
65
55
66bool InputDispatcherFilter::eventFilter(QObject *o, QEvent *e)
56bool InputDispatcherFilter::eventFilter(QObject *o, QEvent *e)
@@ -72,35 +62,63 @@ bool InputDispatcherFilter::eventFilter(QObject *o, QEvent *e)
72 case QEvent::MouseButtonPress:
62 case QEvent::MouseButtonPress:
73 case QEvent::MouseButtonRelease:
63 case QEvent::MouseButtonRelease:
74 {
64 {
75− // FIXME - removed all input filtering for now - this was for extended display
65+ // if we don't have any pointers, filter all mouse events.
66+ auto pointer = currentPointer();
76− return false;
67+ if (!pointer) return true;
77−
68+
78−// QMouseEvent* me = static_cast<QMouseEvent*>(e);
69+ QMouseEvent* me = static_cast<QMouseEvent*>(e);
79−
70+
80−// // Local position gives relative change of mouse pointer.
71+ // Local position gives relative change of mouse pointer.
81−// QPointF localPos = me->localPos();
72+ QPointF movement = me->localPos();
82−// QPointF globalPos = me->screenPos();
73+
83−
84−// // Adjust the position
74+ // Adjust the position
85−// QPointF oldPos(mousePosition.isNull() ? globalPos : mousePosition);
75+ QPointF oldPos = pointer->window()->geometry().topLeft() + pointer->position();
86−// QPointF newPos = adjustedPositionForMovement(oldPos, localPos);
76+ QPointF newPos = adjustedPositionForMovement(oldPos, movement);
87−
77+
88−// QScreen* currentScreen = screenAt(newPos);
78+ QScreen* currentScreen = screenAt(newPos);
89−// if (currentScreen) {
79+ if (currentScreen) {
90−// QRect screenRect = currentScreen->geometry();
80+ QRect screenRect = currentScreen->geometry();
91−// qreal unadjustedX = (oldPos + localPos).x();
81+ qreal newX = (oldPos + movement).x();
92−// if (unadjustedX < screenRect.left()) {
82+ qreal newY = (oldPos + movement).y();
83+
84+ if (newX <= screenRect.left() && newY < screenRect.top() + pointer->topBoundaryOffset()) { // top left corner
85+ const auto distance = qSqrt(qPow(newX, 2) + qPow(newY- screenRect.top() - pointer->topBoundaryOffset(), 2));
86+ Q_EMIT pushedTopLeftCorner(currentScreen, qAbs(distance), me->buttons());
87+ m_pushing = true;
88+ } else if (newX >= screenRect.right()-1 && newY < screenRect.top() + pointer->topBoundaryOffset()) { // top right corner
89+ const auto distance = qSqrt(qPow(newX-screenRect.right(), 2) + qPow(newY - screenRect.top() - pointer->topBoundaryOffset(), 2));
90+ Q_EMIT pushedTopRightCorner(currentScreen, qAbs(distance), me->buttons());
91+ m_pushing = true;
92+ } else if (newX < 0 && newY >= screenRect.bottom()-1) { // bottom left corner
93+ const auto distance = qSqrt(qPow(newX, 2) + qPow(newY-screenRect.bottom(), 2));
94+ Q_EMIT pushedBottomLeftCorner(currentScreen, qAbs(distance), me->buttons());
95+ m_pushing = true;
96+ } else if (newX >= screenRect.right()-1 && newY >= screenRect.bottom()-1) { // bottom right corner
97+ const auto distance = qSqrt(qPow(newX-screenRect.right(), 2) + qPow(newY-screenRect.bottom(), 2));
98+ Q_EMIT pushedBottomRightCorner(currentScreen, qAbs(distance), me->buttons());
99+ m_pushing = true;
100+ } else if (newX < screenRect.left()) { // left edge
93−// Q_EMIT pushedLeftBoundary(currentScreen, qAbs(unadjustedX - screenRect.left()), me->buttons());
101+ Q_EMIT pushedLeftBoundary(currentScreen, qAbs(newX), me->buttons());
102+ m_pushing = true;
94−// } else if (unadjustedX > screenRect.right()) {
103+ } else if (newX >= screenRect.right()) { // right edge
95−// Q_EMIT pushedRightBoundary(currentScreen, qAbs(unadjustedX - screenRect.right()), me->buttons());
104+ Q_EMIT pushedRightBoundary(currentScreen, newX - (screenRect.right() - 1), me->buttons());
105+ m_pushing = true;
106+ } else if (newY < screenRect.top() + pointer->topBoundaryOffset()) { // top edge
107+ Q_EMIT pushedTopBoundary(currentScreen, qAbs(newY - screenRect.top() - pointer->topBoundaryOffset()), me->buttons());
108+ m_pushing = true;
109+ } else if (Q_LIKELY(newX > 0 && newX < screenRect.right()-1 && newY > 0 && newY < screenRect.bottom()-1)) { // normal pos, not pushing
110+ if (m_pushing) {
111+ Q_EMIT pushStopped(currentScreen);
112+ m_pushing = false;
113+ }
96−// }
114+ }
97−// }
115+ }
98−
116+
99−// // Send the event
117+ // Send the event
100−// QMouseEvent eCopy(me->type(), me->localPos(), newPos, me->button(), me->buttons(), me->modifiers());
118+ QMouseEvent eCopy(me->type(), me->localPos(), newPos, me->button(), me->buttons(), me->modifiers());
101−// eCopy.setTimestamp(me->timestamp());
119+ eCopy.setTimestamp(me->timestamp());
102−// o->event(&eCopy);
120+ o->event(&eCopy);
103−// return true;
121+ return true;
104 }
122 }
105 default:
123 default:
106 break;
124 break;
@@ -112,7 +130,7 @@ QPointF InputDispatcherFilter::adjustedPositionForMovement(const QPointF &pt, co
112{
130{
113 QPointF adjusted = pt + movement;
131 QPointF adjusted = pt + movement;
114
132
115− auto screen = screenAt(adjusted); // first check if our move was to a valid screen.
133+ auto screen = screenAt(adjusted); // first check if our move was to a screen with an enabled pointer.
116 if (screen) {
134 if (screen) {
117 return adjusted;
135 return adjusted;
118 } else if ((screen = screenAt(pt))) { // then check if our old position was valid
136 } else if ((screen = screenAt(pt))) { // then check if our old position was valid
@@ -126,7 +144,7 @@ QPointF InputDispatcherFilter::adjustedPositionForMovement(const QPointF &pt, co
126 // center of first screen with a pointer.
144 // center of first screen with a pointer.
127 Q_FOREACH(QScreen* screen, screens) {
145 Q_FOREACH(QScreen* screen, screens) {
128 Q_FOREACH(MousePointer* pointer, m_pointers) {
146 Q_FOREACH(MousePointer* pointer, m_pointers) {
129− if (pointer->screen() == screen) {
147+ if (pointer->isEnabled() && pointer->screen() == screen) {
130 return screen->geometry().center();
148 return screen->geometry().center();
131 }
149 }
132 }
150 }
@@ -138,9 +156,20 @@ QPointF InputDispatcherFilter::adjustedPositionForMovement(const QPointF &pt, co
138QScreen *InputDispatcherFilter::screenAt(const QPointF &pt) const
156QScreen *InputDispatcherFilter::screenAt(const QPointF &pt) const
139{
157{
140 Q_FOREACH(MousePointer* pointer, m_pointers) {
158 Q_FOREACH(MousePointer* pointer, m_pointers) {
159+ if (!pointer->isEnabled()) continue;
160+
141 QScreen* screen = pointer->screen();
161 QScreen* screen = pointer->screen();
142 if (screen && screen->geometry().contains(pt.toPoint()))
162 if (screen && screen->geometry().contains(pt.toPoint()))
143 return screen;
163 return screen;
144 }
164 }
145 return nullptr;
165 return nullptr;
146}
166}
167+
168+MousePointer *InputDispatcherFilter::currentPointer() const
169+{
170+ Q_FOREACH(MousePointer* pointer, m_pointers) {
171+ if (!pointer->isEnabled()) continue;
172+ return pointer;
173+ }
174+ return nullptr;
175+}
plugins/Cursor/InputDispatcherFilter.h+9−4
@@ -31,14 +31,17 @@ public:
31 static InputDispatcherFilter *instance();
31 static InputDispatcherFilter *instance();
32
32
33 void registerPointer(MousePointer* pointer);
33 void registerPointer(MousePointer* pointer);
34−
35 void unregisterPointer(MousePointer* pointer);
34 void unregisterPointer(MousePointer* pointer);
36
35
37− void setPosition(const QPointF &pos);
38−
39Q_SIGNALS:
36Q_SIGNALS:
40 void pushedLeftBoundary(QScreen* screen, qreal amount, Qt::MouseButtons buttons);
37 void pushedLeftBoundary(QScreen* screen, qreal amount, Qt::MouseButtons buttons);
41 void pushedRightBoundary(QScreen* screen, qreal amount, Qt::MouseButtons buttons);
38 void pushedRightBoundary(QScreen* screen, qreal amount, Qt::MouseButtons buttons);
39+ void pushedTopBoundary(QScreen* screen, qreal amount, Qt::MouseButtons buttons);
40+ void pushedTopLeftCorner(QScreen* screen, qreal amount, Qt::MouseButtons buttons);
41+ void pushedTopRightCorner(QScreen* screen, qreal amount, Qt::MouseButtons buttons);
42+ void pushedBottomLeftCorner(QScreen* screen, qreal amount, Qt::MouseButtons buttons);
43+ void pushedBottomRightCorner(QScreen* screen, qreal amount, Qt::MouseButtons buttons);
44+ void pushStopped(QScreen* screen);
42
45
43protected:
46protected:
44 InputDispatcherFilter(QObject* parent = nullptr);
47 InputDispatcherFilter(QObject* parent = nullptr);
@@ -48,10 +51,12 @@ protected:
48 QPointF adjustedPositionForMovement(const QPointF& pt, const QPointF& movement) const;
51 QPointF adjustedPositionForMovement(const QPointF& pt, const QPointF& movement) const;
49 QScreen* screenAt(const QPointF& pt) const;
52 QScreen* screenAt(const QPointF& pt) const;
50
53
54+ MousePointer* currentPointer() const;
55+
51private:
56private:
52 QObject* m_inputDispatcher;
57 QObject* m_inputDispatcher;
53 QSet<MousePointer*> m_pointers;
58 QSet<MousePointer*> m_pointers;
54− QPointF mousePosition;
59+ bool m_pushing;
55};
60};
56
61
57#endif // INPUTDISPATCHERFILTER_H
62#endif // INPUTDISPATCHERFILTER_H
plugins/Cursor/MousePointer.cpp+45−25
@@ -28,34 +28,56 @@ MousePointer::MousePointer(QQuickItem *parent)
28 , m_cursorName(QStringLiteral("left_ptr"))
28 , m_cursorName(QStringLiteral("left_ptr"))
29 , m_themeName(QStringLiteral("default"))
29 , m_themeName(QStringLiteral("default"))
30{
30{
31− InputDispatcherFilter::instance()->registerPointer(this);
32−
33− auto mouseMovedFunc = [this]() {
34− if (!isEnabled() || !window()) return;
35− QPointF globalPosition = mapToItem(nullptr, QPointF(0, 0));
36− InputDispatcherFilter::instance()->setPosition(globalPosition);
37− Q_EMIT mouseMoved();
38− };
39− connect(this, &QQuickItem::xChanged, this, mouseMovedFunc);
40− connect(this, &QQuickItem::yChanged, this, mouseMovedFunc);
41−
42− connect(this, &QQuickItem::enabledChanged, this, [this]() {
43− if (!isEnabled()) setVisible(false);
44− });
45−
46 connect(InputDispatcherFilter::instance(), &InputDispatcherFilter::pushedLeftBoundary,
31 connect(InputDispatcherFilter::instance(), &InputDispatcherFilter::pushedLeftBoundary,
47 this, [this](QScreen* screen, qreal amount, Qt::MouseButtons buttons) {
32 this, [this](QScreen* screen, qreal amount, Qt::MouseButtons buttons) {
48 if (window() && window()->screen() == screen) {
33 if (window() && window()->screen() == screen) {
49 Q_EMIT pushedLeftBoundary(amount, buttons);
34 Q_EMIT pushedLeftBoundary(amount, buttons);
50 }
35 }
51 });
36 });
52−
53 connect(InputDispatcherFilter::instance(), &InputDispatcherFilter::pushedRightBoundary,
37 connect(InputDispatcherFilter::instance(), &InputDispatcherFilter::pushedRightBoundary,
54 this, [this](QScreen* screen, qreal amount, Qt::MouseButtons buttons) {
38 this, [this](QScreen* screen, qreal amount, Qt::MouseButtons buttons) {
55 if (window() && window()->screen() == screen) {
39 if (window() && window()->screen() == screen) {
56 Q_EMIT pushedRightBoundary(amount, buttons);
40 Q_EMIT pushedRightBoundary(amount, buttons);
57 }
41 }
58 });
42 });
43+ connect(InputDispatcherFilter::instance(), &InputDispatcherFilter::pushedTopBoundary,
44+ this, [this](QScreen* screen, qreal amount, Qt::MouseButtons buttons) {
45+ if (window() && window()->screen() == screen) {
46+ Q_EMIT pushedTopBoundary(amount, buttons);
47+ }
48+ });
49+ connect(InputDispatcherFilter::instance(), &InputDispatcherFilter::pushedTopLeftCorner,
50+ this, [this](QScreen* screen, qreal amount, Qt::MouseButtons buttons) {
51+ if (window() && window()->screen() == screen) {
52+ Q_EMIT pushedTopLeftCorner(amount, buttons);
53+ }
54+ });
55+ connect(InputDispatcherFilter::instance(), &InputDispatcherFilter::pushedTopRightCorner,
56+ this, [this](QScreen* screen, qreal amount, Qt::MouseButtons buttons) {
57+ if (window() && window()->screen() == screen) {
58+ Q_EMIT pushedTopRightCorner(amount, buttons);
59+ }
60+ });
61+ connect(InputDispatcherFilter::instance(), &InputDispatcherFilter::pushedBottomLeftCorner,
62+ this, [this](QScreen* screen, qreal amount, Qt::MouseButtons buttons) {
63+ if (window() && window()->screen() == screen) {
64+ Q_EMIT pushedBottomLeftCorner(amount, buttons);
65+ }
66+ });
67+ connect(InputDispatcherFilter::instance(), &InputDispatcherFilter::pushedBottomRightCorner,
68+ this, [this](QScreen* screen, qreal amount, Qt::MouseButtons buttons) {
69+ if (window() && window()->screen() == screen) {
70+ Q_EMIT pushedBottomRightCorner(amount, buttons);
71+ }
72+ });
73+ connect(InputDispatcherFilter::instance(), &InputDispatcherFilter::pushStopped,
74+ this, [this](QScreen* screen) {
75+ if (window() && window()->screen() == screen) {
76+ Q_EMIT pushStopped();
77+ }
78+ });
79+
80+ InputDispatcherFilter::instance()->registerPointer(this);
59}
81}
60
82
61MousePointer::~MousePointer()
83MousePointer::~MousePointer()
@@ -64,11 +86,6 @@ MousePointer::~MousePointer()
64 InputDispatcherFilter::instance()->unregisterPointer(this);
86 InputDispatcherFilter::instance()->unregisterPointer(this);
65}
87}
66
88
67−void MousePointer::handleMouseEvent(ulong /*timestamp*/, QPointF /*movement*/, Qt::MouseButtons /*buttons*/,
68− Qt::KeyboardModifiers /*modifiers*/)
69−{
70−}
71−
72void MousePointer::applyItemConfinement(qreal &newX, qreal &newY)
89void MousePointer::applyItemConfinement(qreal &newX, qreal &newY)
73{
90{
74 Q_ASSERT(parentItem() != nullptr);
91 Q_ASSERT(parentItem() != nullptr);
@@ -94,10 +111,6 @@ void MousePointer::applyItemConfinement(qreal &newX, qreal &newY)
94 }
111 }
95}
112}
96
113
97−void MousePointer::handleWheelEvent(ulong /*timestamp*/, QPoint /*angleDelta*/, Qt::KeyboardModifiers /*modifiers*/)
98−{
99−}
100−
101int MousePointer::topBoundaryOffset() const
114int MousePointer::topBoundaryOffset() const
102{
115{
103 return m_topBoundaryOffset;
116 return m_topBoundaryOffset;
@@ -182,6 +195,12 @@ void MousePointer::setThemeName(const QString &themeName)
182 }
195 }
183}
196}
184
197
198+void MousePointer::moveTo(const QPoint &position)
199+{
200+ setPosition(position);
201+ Q_EMIT mouseMoved();
202+}
203+
185void MousePointer::setCustomCursor(const QCursor &customCursor)
204void MousePointer::setCustomCursor(const QCursor &customCursor)
186{
205{
187 CursorImageProvider::instance()->setCustomCursor(customCursor);
206 CursorImageProvider::instance()->setCustomCursor(customCursor);
@@ -199,3 +218,4 @@ void MousePointer::setConfiningItem(QQuickItem *item)
199 Q_EMIT confiningItemChanged();
218 Q_EMIT confiningItemChanged();
200 }
219 }
201}
220}
221+
plugins/Cursor/MousePointer.h+3−5
@@ -39,6 +39,8 @@ public:
39 void setThemeName(const QString &themeName) override;
39 void setThemeName(const QString &themeName) override;
40 QString themeName() const override { return m_themeName; }
40 QString themeName() const override { return m_themeName; }
41
41
42+ void moveTo(const QPoint& position) override;
43+
42 void setCustomCursor(const QCursor &) override;
44 void setCustomCursor(const QCursor &) override;
43
45
44 QQuickItem* confiningItem() const;
46 QQuickItem* confiningItem() const;
@@ -49,11 +51,6 @@ public:
49
51
50 QScreen* screen() const { return m_registeredScreen; }
52 QScreen* screen() const { return m_registeredScreen; }
51
53
52−public Q_SLOTS:
53− void handleMouseEvent(ulong timestamp, QPointF movement, Qt::MouseButtons buttons,
54− Qt::KeyboardModifiers modifiers) override;
55− void handleWheelEvent(ulong timestamp, QPoint angleDelta, Qt::KeyboardModifiers modifiers) override;
56−
57Q_SIGNALS:
54Q_SIGNALS:
58 void pushedLeftBoundary(qreal amount, Qt::MouseButtons buttons);
55 void pushedLeftBoundary(qreal amount, Qt::MouseButtons buttons);
59 void pushedRightBoundary(qreal amount, Qt::MouseButtons buttons);
56 void pushedRightBoundary(qreal amount, Qt::MouseButtons buttons);
@@ -82,6 +79,7 @@ private:
82 QPointer<QScreen> m_registeredScreen;
79 QPointer<QScreen> m_registeredScreen;
83 QString m_cursorName;
80 QString m_cursorName;
84 QString m_themeName;
81 QString m_themeName;
82+ bool m_active;
85
83
86 // Accumulated, unapplied, mouse movement.
84 // Accumulated, unapplied, mouse movement.
87 QPointF m_accumulatedMovement;
85 QPointF m_accumulatedMovement;
plugins/Unity/CMakeLists.txt+1
@@ -1,5 +1,6 @@
1add_subdirectory(ApplicationMenu)
1add_subdirectory(ApplicationMenu)
2add_subdirectory(Connectivity)
2add_subdirectory(Connectivity)
3+add_subdirectory(Debug)
3add_subdirectory(Indicators)
4add_subdirectory(Indicators)
4add_subdirectory(Launcher)
5add_subdirectory(Launcher)
5add_subdirectory(Session)
6add_subdirectory(Session)
plugins/Unity/Debug/CMakeLists.txtnew file+30
@@ -0,0 +1,30 @@
1+include_directories(
2+ ${CMAKE_CURRENT_SOURCE_DIR}
3+ ${CMAKE_CURRENT_BINARY_DIR}
4+)
5+
6+include_directories(
7+ SYSTEM
8+ ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
9+ ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
10+ ${GIO_INCLUDE_DIRS}
11+)
12+
13+set(QMLPLUGIN_SRC
14+ consolelog.cpp
15+ plugin.cpp
16+ )
17+
18+add_library(Debug-qml SHARED
19+ ${QMLPLUGIN_SRC}
20+ )
21+
22+# Because this is an internal support library, we want
23+# to expose all symbols in it. Consider changing this
24+# either to a static library or just using the
25+# files directly in targets.
26+set_target_properties(Debug-qml PROPERTIES COMPILE_FLAGS -fvisibility=default)
27+
28+qt5_use_modules(Debug-qml Qml Quick Concurrent)
29+
30+add_unity8_plugin(Unity.Debug 0.1 Unity/Debug TARGETS Debug-qml)
plugins/Unity/Debug/consolelog.cppnew file+221
@@ -0,0 +1,221 @@
1+/*
2+ * Copyright 2016 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
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/>.
15+*/
16+
17+#include "consolelog.h"
18+
19+#include <unistd.h>
20+#include <stdio.h>
21+#include <thread>
22+#include <iostream>
23+#include <sys/ioctl.h>
24+
25+#include <functional>
26+
27+namespace
28+{
29+
30+int wait_fd(std::function<int(void)> fn)
31+{
32+ int ret = -1;
33+ bool fd_blocked = false;
34+ do
35+ {
36+ ret = fn();
37+ fd_blocked = (errno == EINTR || errno == EBUSY);
38+ if (fd_blocked)
39+ QThread::msleep(10);
40+ }
41+ while (ret < 0);
42+ return ret;
43+}
44+
45+#define PIPE_WAIT(val) wait_fd(std::bind(pipe, val))
46+#define DUP_WAIT(fd) wait_fd(std::bind(dup, fd))
47+#define DUP2_WAIT(fd1, fd2) wait_fd(std::bind(dup2, fd1, fd2))
48+#define CLOSE_WAIT(fd) wait_fd(std::bind(close, fd))
49+
50+}
51+
52+LogRedirector::LogRedirector()
53+ : m_ref(0)
54+ , m_stop(false)
55+{
56+ setObjectName("ConsoleLog");
57+
58+ // make stdout & stderr streams unbuffered
59+ // so that we don't need to flush the streams
60+ // before capture and after capture
61+ // (fflush can cause a deadlock if the stream is currently being used)
62+ setvbuf(stdout,NULL,_IONBF,0);
63+ setvbuf(stderr,NULL,_IONBF,0);
64+}
65+
66+void LogRedirector::run()
67+{
68+ PIPE_WAIT(m_pipe);
69+
70+ int oldStdOut = DUP_WAIT(fileno(stdout));
71+ int oldStdErr = DUP_WAIT(fileno(stderr));
72+
73+ DUP2_WAIT(m_pipe[WRITE], fileno(stdout));
74+ DUP2_WAIT(m_pipe[WRITE], fileno(stderr));
75+ CLOSE_WAIT(m_pipe[WRITE]);
76+
77+ while(true) {
78+ {
79+ QMutexLocker lock(&m_mutex);
80+ if (m_stop) break;
81+ }
82+ checkLog();
83+ QThread::msleep(50);
84+ }
85+
86+ DUP2_WAIT(oldStdOut, fileno(stdout));
87+ DUP2_WAIT(oldStdErr, fileno(stderr));
88+
89+ CLOSE_WAIT(oldStdOut);
90+ CLOSE_WAIT(oldStdErr);
91+ CLOSE_WAIT(m_pipe[READ]);
92+}
93+
94+
95+void LogRedirector::checkLog()
96+{
97+ // Do not allow read to block with no data.
98+ // If we stop the thread while waiting a read,
99+ // it will block the main thread waiting for this thread to stop.
100+ int count = 0;
101+ ioctl(m_pipe[READ], FIONREAD, &count);
102+ if (count <= 0) return;
103+
104+ std::string captured;
105+ std::string buf;
106+ const int bufSize = 1024;
107+ buf.resize(bufSize);
108+ int bytesRead = 0;
109+ bytesRead = read(m_pipe[READ], &(*buf.begin()), bufSize);
110+ while(bytesRead == bufSize)
111+ {
112+ captured += buf;
113+ bytesRead = 0;
114+ bytesRead = read(m_pipe[READ], &(*buf.begin()), bufSize);
115+ }
116+ if (bytesRead > 0)
117+ {
118+ buf.resize(bytesRead);
119+ captured += buf;
120+ }
121+
122+ if (!captured.empty()) {
123+ Q_EMIT log(QString::fromStdString(captured));
124+ }
125+}
126+
127+LogRedirector *LogRedirector::instance()
128+{
129+ static LogRedirector* log = nullptr;
130+ if (!log) {
131+ log = new LogRedirector();
132+ }
133+ return log;
134+}
135+
136+void LogRedirector::add(ConsoleLog* logger)
137+{
138+ QMutexLocker lock(&m_mutex);
139+ connect(this, &LogRedirector::log, logger, &ConsoleLog::logged, Qt::UniqueConnection);
140+
141+ m_ref++;
142+ if (!LogRedirector::instance()->isRunning()) {
143+ m_stop = false;
144+ LogRedirector::instance()->start();
145+ }
146+}
147+
148+void LogRedirector::remove(ConsoleLog* logger)
149+{
150+ QMutexLocker lock(&m_mutex);
151+ disconnect(this, &LogRedirector::log, logger, &ConsoleLog::logged);
152+
153+ m_ref = qMax(m_ref-1, 0);
154+ if (m_ref == 0 && LogRedirector::instance()->isRunning()) {
155+ m_stop = true;
156+ lock.unlock();
157+ LogRedirector::instance()->wait();
158+ }
159+}
160+
161+ConsoleLog::ConsoleLog(QObject *parent)
162+ : QObject(parent)
163+ , m_enabled(false)
164+ , m_maxLines(60)
165+{
166+ auto updateEnabled = [this]() {
167+ if (m_enabled) {
168+ LogRedirector::instance()->add(this);
169+ } else {
170+ LogRedirector::instance()->remove(this);
171+ }
172+ };
173+ connect(this, &ConsoleLog::enabledChanged, this, updateEnabled);
174+}
175+
176+ConsoleLog::~ConsoleLog()
177+{
178+ if (m_enabled) {
179+ LogRedirector::instance()->remove(this);
180+ }
181+}
182+
183+void ConsoleLog::setEnabled(bool enabled)
184+{
185+ if (m_enabled == enabled) {
186+ return;
187+ }
188+
189+ m_enabled = enabled;
190+ Q_EMIT enabledChanged();
191+}
192+
193+QString ConsoleLog::out() const
194+{
195+ return m_out.join("\n");
196+}
197+
198+void ConsoleLog::setMaxLines(int maxLines)
199+{
200+ if (m_maxLines == maxLines) {
201+ return;
202+ }
203+
204+ m_maxLines = maxLines;
205+ while (m_out.count() > m_maxLines) {
206+ m_out.removeLast();
207+ }
208+ Q_EMIT outChanged();
209+ Q_EMIT maxLinesChanged();
210+}
211+
212+void ConsoleLog::logged(QString captured)
213+{
214+ QStringList li = captured.split("\n", QString::SkipEmptyParts);
215+ li << m_out;
216+ m_out = li;
217+ while (m_out.count() > m_maxLines) {
218+ m_out.removeLast();
219+ }
220+ Q_EMIT outChanged();
221+}
plugins/Unity/Debug/consolelog.hnew file+87
@@ -0,0 +1,87 @@
1+/*
2+ * Copyright 2016 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
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/>.
15+*/
16+
17+#ifndef CONSOLELOG_H
18+#define CONSOLELOG_H
19+
20+#include <QThread>
21+#include <QMutex>
22+#include <QSet>
23+
24+class ConsoleLog;
25+
26+class LogRedirector : public QThread
27+{
28+ Q_OBJECT
29+public:
30+ static LogRedirector *instance();
31+
32+ void add(ConsoleLog* logger);
33+ void remove(ConsoleLog* logger);
34+
35+private Q_SLOTS:
36+ void checkLog();
37+
38+Q_SIGNALS:
39+ void log(QString log);
40+
41+private:
42+ LogRedirector();
43+ void run() Q_DECL_OVERRIDE;
44+
45+ QMutex m_mutex;
46+ int m_pipe[2];
47+ int m_ref;
48+ bool m_stop;
49+
50+ enum PIPES { READ, WRITE };
51+};
52+
53+class ConsoleLog : public QObject
54+{
55+ Q_OBJECT
56+ Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged)
57+ Q_PROPERTY(QString out READ out NOTIFY outChanged)
58+ Q_PROPERTY(int maxLines READ maxLines WRITE setMaxLines NOTIFY maxLinesChanged)
59+public:
60+ explicit ConsoleLog(QObject *parent = 0);
61+ ~ConsoleLog();
62+
63+ bool isEnabled() const { return m_enabled; }
64+ void setEnabled(bool enabled);
65+
66+ QString out() const;
67+
68+ int maxLines() const { return m_maxLines; }
69+ void setMaxLines(int maxLines);
70+
71+public Q_SLOTS:
72+ void logged(QString captured);
73+
74+Q_SIGNALS:
75+ void enabledChanged();
76+ void outChanged();
77+ void maxLinesChanged();
78+
79+private:
80+ void updateEnabled();
81+
82+ QStringList m_out;
83+ bool m_enabled;
84+ int m_maxLines;
85+};
86+
87+#endif // CONSOLELOG_H
plugins/Unity/Debug/plugin.cppnew file+31
@@ -0,0 +1,31 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+// Qt
18+#include <QtQml/qqml.h>
19+
20+// self
21+#include "plugin.h"
22+
23+// local
24+#include "consolelog.h"
25+
26+void UnityDebugPlugin::registerTypes(const char *uri)
27+{
28+ Q_ASSERT(uri == QLatin1String("Unity.Debug"));
29+
30+ qmlRegisterType<ConsoleLog>(uri, 0, 1, "ConsoleLog");
31+}
plugins/Unity/Debug/plugin.hnew file+32
@@ -0,0 +1,32 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef UNITYDEBUG_PLUGIN_H
18+#define UNITYDEBUG_PLUGIN_H
19+
20+#include <QtQml/QQmlEngine>
21+#include <QtQml/QQmlExtensionPlugin>
22+
23+class UnityDebugPlugin : public QQmlExtensionPlugin
24+{
25+ Q_OBJECT
26+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
27+
28+public:
29+ void registerTypes(const char *uri) override;
30+};
31+
32+#endif
plugins/Unity/Debug/qmldirnew file+3
@@ -0,0 +1,3 @@
1+module Unity.Debug
2+plugin Debug-qml
3+typeinfo Debug.qmltypes
qml/Shell.qml+38−1
@@ -45,6 +45,7 @@ import Unity.DashCommunicator 0.1
45import Unity.Indicators 0.1 as Indicators
45import Unity.Indicators 0.1 as Indicators
46import Cursor 1.1
46import Cursor 1.1
47import WindowManager 1.0
47import WindowManager 1.0
48+import Unity.Debug 0.1 as Debug
48
49
49
50
50StyledItem {
51StyledItem {
@@ -251,6 +252,7 @@ StyledItem {
251 var mappedCoords = mapFromItem(null, pos.x, pos.y);
252 var mappedCoords = mapFromItem(null, pos.x, pos.y);
252 cursor.x = mappedCoords.x;
253 cursor.x = mappedCoords.x;
253 cursor.y = mappedCoords.y;
254 cursor.y = mappedCoords.y;
255+ cursor.mouseNeverMoved = false;
254 }
256 }
255 }
257 }
256
258
@@ -320,6 +322,11 @@ StyledItem {
320 panel.indicators.hide();
322 panel.indicators.hide();
321 panel.applicationMenus.hide();
323 panel.applicationMenus.hide();
322 }
324 }
325+ Binding {
326+ target: applicationsDisplayLoader.item
327+ property: "oskEnabled"
328+ value: shell.oskEnabled
329+ }
323 }
330 }
324 }
331 }
325
332
@@ -714,10 +721,23 @@ StyledItem {
714
721
715 Cursor {
722 Cursor {
716 id: cursor
723 id: cursor
717− visible: shell.hasMouse
724+ objectName: "Pointer-"+screenWindow.objectName
725+
718 z: itemGrabber.z + 1
726 z: itemGrabber.z + 1
719 opacity: 0
727 opacity: 0
720 topBoundaryOffset: panel.panelHeight
728 topBoundaryOffset: panel.panelHeight
729+ enabled: shell.hasMouse && screenWindow.active
730+ visible: enabled
731+
732+ property bool mouseNeverMoved: true
733+ Binding {
734+ target: cursor; property: "x"; value: shell.width / 2
735+ when: cursor.mouseNeverMoved && cursor.visible
736+ }
737+ Binding {
738+ target: cursor; property: "y"; value: shell.height / 2
739+ when: cursor.mouseNeverMoved && cursor.visible
740+ }
721
741
722 confiningItem: stage.itemConfiningMouseCursor
742 confiningItem: stage.itemConfiningMouseCursor
723
743
@@ -775,6 +795,7 @@ StyledItem {
775 }
795 }
776
796
777 onMouseMoved: {
797 onMouseMoved: {
798+ mouseNeverMoved = false;
778 cursor.opacity = 1;
799 cursor.opacity = 1;
779 }
800 }
780
801
@@ -806,4 +827,20 @@ StyledItem {
806 }
827 }
807 }
828 }
808 }
829 }
830+
831+ Loader {
832+ z: shutdownFadeOutRectangle.z + 1
833+ active: DebuggingController.logOverlay
834+
835+ sourceComponent: Text {
836+ width: shell.width
837+ height: shell.height
838+
839+ text: consoleLog.out
840+ Debug.ConsoleLog {
841+ id: consoleLog
842+ enabled: true
843+ }
844+ }
845+ }
809}
846}
src/DebuggingController.cpp+12−1
@@ -49,7 +49,8 @@ public:
49};
49};
50
50
51DebuggingController::DebuggingController(QObject *parent):
51DebuggingController::DebuggingController(QObject *parent):
52− UnityDBusObject(QStringLiteral("/com/canonical/Unity8/Debugging"), QStringLiteral("com.canonical.Unity8"), true, parent)
52+ UnityDBusObject(QStringLiteral("/com/canonical/Unity8/Debugging"), QStringLiteral("com.canonical.Unity8"), true, parent),
53+ m_logOverlay(true)
53{
54{
54}
55}
55
56
@@ -90,3 +91,13 @@ void DebuggingController::SetLoggingFilterRules(const QString &filterRules)
90{
91{
91 QLoggingCategory::setFilterRules(filterRules);
92 QLoggingCategory::setFilterRules(filterRules);
92}
93}
94+
95+void DebuggingController::SetLogOverlay(bool logOverlay)
96+{
97+ if (logOverlay == m_logOverlay) {
98+ return;
99+ }
100+
101+ m_logOverlay = logOverlay;
102+ Q_EMIT logOverlayChanged(m_logOverlay);
103+}
src/DebuggingController.h+14
@@ -32,10 +32,14 @@ class DebuggingController: public UnityDBusObject
32 Q_OBJECT
32 Q_OBJECT
33 Q_CLASSINFO("D-Bus Interface", "com.canonical.Unity8.Debugging")
33 Q_CLASSINFO("D-Bus Interface", "com.canonical.Unity8.Debugging")
34
34
35+ Q_PROPERTY(bool logOverlay READ logOverlay NOTIFY logOverlayChanged)
36+
35public:
37public:
36 DebuggingController(QObject *parent = nullptr);
38 DebuggingController(QObject *parent = nullptr);
37 ~DebuggingController() = default;
39 ~DebuggingController() = default;
38
40
41+ bool logOverlay() const { return m_logOverlay; }
42+
39public Q_SLOTS:
43public Q_SLOTS:
40 /**
44 /**
41 * Set the QSG_VISUALIZE mode. This follows the vlues supported by Qt in
45 * Set the QSG_VISUALIZE mode. This follows the vlues supported by Qt in
@@ -53,5 +57,15 @@ public Q_SLOTS:
53 */
57 */
54 Q_SCRIPTABLE void SetLoggingFilterRules(const QString &filterRules);
58 Q_SCRIPTABLE void SetLoggingFilterRules(const QString &filterRules);
55
59
60+ /**
61+ * Enable/Disable the shell console log overlay
62+ */
63+ Q_SCRIPTABLE void SetLogOverlay(bool logOverlay);
64+
65+Q_SIGNALS:
66+ void logOverlayChanged(bool logOverlay);
67+
68+private:
69+ bool m_logOverlay;
56};
70};
57#endif // DEBUGGINGCONTROLLER_H
71#endif // DEBUGGINGCONTROLLER_H
src/ShellApplication.cpp+1−1
@@ -87,7 +87,7 @@ ShellApplication::ShellApplication(int & argc, char ** argv, bool isMirServer)
87 }
87 }
88 #endif
88 #endif
89
89
90− new DebuggingController(this);
90+ m_qmlEngine->rootContext()->setContextProperty("DebuggingController", new DebuggingController(this));
91
91
92// if (parser.mode().compare("greeter") == 0) {
92// if (parser.mode().compare("greeter") == 0) {
93// QSize primaryScreenSize = this->primaryScreen()->size();
93// QSize primaryScreenSize = this->primaryScreen()->size();
tests/uqmlscene/main.cpp+2
@@ -479,6 +479,8 @@ int main(int argc, char ** argv)
479 // TODO: as soon as the engine construction completes, the debug service is
479 // TODO: as soon as the engine construction completes, the debug service is
480 // listening for connections. But actually we aren't ready to debug anything.
480 // listening for connections. But actually we aren't ready to debug anything.
481 QQmlEngine engine;
481 QQmlEngine engine;
482+ engine.rootContext()->setContextProperty("DebuggingController", new DebuggingController(&app));
483+
482 QQmlComponent *component = new QQmlComponent(&engine);
484 QQmlComponent *component = new QQmlComponent(&engine);
483 for (int i = 0; i < imports.size(); ++i)
485 for (int i = 0; i < imports.size(); ++i)
484 engine.addImportPath(imports.at(i));
486 engine.addImportPath(imports.at(i));