15#include <QQmlIncubator>
17#include <QQuickWindow>
22static QHash<QQuickWindow *, QuickSceneView *> s_views;
28 : QQmlIncubator(QQmlIncubator::Asynchronous)
31 , m_statusChangedCallback(statusChangedCallback)
35 std::unique_ptr<QuickSceneView>
result()
37 return std::move(m_view);
42 m_view = std::make_unique<QuickSceneView>(m_effect, m_screen);
43 m_view->setAutomaticRepaint(
false);
44 m_view->setRootItem(qobject_cast<QQuickItem *>(
object));
49 m_statusChangedCallback(
this);
56 std::unique_ptr<QuickSceneView> m_view;
64 return effect->d.get();
70 std::map<Output *, std::unique_ptr<QQmlContext>>
contexts;
71 std::map<Output *, std::unique_ptr<QQmlIncubator>>
incubators;
72 std::map<Output *, std::unique_ptr<QuickSceneView>>
views;
79 if (!item || !screen || !
views.contains(screen)) {
83 const auto &view =
views.at(screen);
84 return item->window() == view->window();
97 s_views.insert(
window(),
this);
107 return m_rootItem.get();
112 Q_ASSERT_X(item,
"setRootItem",
"root item cannot be null");
113 m_rootItem.reset(item);
116 auto updateSize = [
this]() {
120 connect(
contentItem(), &QQuickItem::widthChanged, m_rootItem.get(), updateSize);
121 connect(
contentItem(), &QQuickItem::heightChanged, m_rootItem.get(), updateSize);
157 return s_views.value(item->window());
162 QQuickItem *item = qobject_cast<QQuickItem *>(
object);
168 qCWarning(LIBKWINEFFECTS) <<
"Could not find SceneView for" << object;
189 const QRectF globalGeom = QRectF(item->mapToGlobal(QPointF(0, 0)), QSizeF(item->width(), item->height()));
190 QList<Output *> screens;
192 for (
const auto &[screen, view] : d->views) {
193 if (!d->isItemOnScreen(item, screen) && screen->geometry().intersects(globalGeom.toRect())) {
203 const auto it = std::find_if(d->views.begin(), d->views.end(), [
this, globalPos, item](
const auto &view) {
204 Output *screen = view.first;
205 return !d->isItemOnScreen(item, screen) && screen->geometry().contains(globalPos.toPoint());
207 if (it != d->views.end()) {
214 if (event->type() == QEvent::CursorChange) {
215 if (
const QWindow *window = qobject_cast<QWindow *>(watched)) {
229 if (d->running != running) {
246 qWarning() <<
"Cannot change QuickSceneEffect.source while running";
249 if (d->source != url) {
257 return d->delegate.get();
263 qWarning() <<
"Cannot change QuickSceneEffect.source while running";
266 if (d->delegate.get() !=
delegate) {
275 const auto it = d->views.find(screen);
276 return it == d->views.end() ? nullptr : it->second.get();
281 const auto it = std::find_if(d->views.begin(), d->views.end(), [pos](
const auto &view) {
282 return view.second->geometry().contains(pos);
284 return it == d->views.end() ? nullptr : it->second.get();
289 auto it = std::find_if(d->views.begin(), d->views.end(), [](
const auto &view) {
290 return view.second->window()->activeFocusItem();
292 if (it == d->views.end()) {
295 return it == d->views.end() ? nullptr : it->second.get();
304 for (
const auto &[screen, view] : d->views) {
307 if (view->geometry().left() < screenView->geometry().left()) {
309 if (!candidate || view->
geometry().left() > candidate->
geometry().left() || (view->geometry().left() == candidate->
geometry().left() && view->geometry().top() > candidate->
geometry().top())) {
310 candidate = view.get();
315 if (view->geometry().top() < screenView->geometry().top()) {
316 if (!candidate || view->
geometry().top() > candidate->
geometry().top() || (view->geometry().top() == candidate->
geometry().top() && view->geometry().left() > candidate->
geometry().left())) {
317 candidate = view.get();
322 if (view->geometry().right() > screenView->geometry().right()) {
323 if (!candidate || view->
geometry().right() < candidate->
geometry().right() || (view->geometry().right() == candidate->
geometry().right() && view->geometry().top() > candidate->
geometry().top())) {
324 candidate = view.get();
329 if (view->geometry().bottom() > screenView->geometry().bottom()) {
330 if (!candidate || view->
geometry().bottom() < candidate->
geometry().bottom() || (view->geometry().bottom() == candidate->
geometry().bottom() && view->geometry().left() > candidate->
geometry().left())) {
331 candidate = view.get();
349 if (view == av && av->
window()->activeFocusItem()) {
353 for (
const auto &[screen, otherView] : d->views) {
354 if (otherView.get() == view && !view->
window()->activeFocusItem()) {
355 QFocusEvent focusEvent(QEvent::FocusIn, Qt::ActiveWindowFocusReason);
356 qApp->sendEvent(view->
window(), &focusEvent);
357 }
else if (otherView.get() != view && otherView->
window()->activeFocusItem()) {
358 QFocusEvent focusEvent(QEvent::FocusOut, Qt::ActiveWindowFocusReason);
359 qApp->sendEvent(otherView->window(), &focusEvent);
370 const auto it = d->views.find(data.
screen);
371 if (it != d->views.end() && it->second->isDirty()) {
372 it->second->resetDirty();
373 it->second->update();
376 for (
const auto &[screen, screenView] : d->views) {
377 if (screenView->isDirty()) {
378 screenView->resetDirty();
379 screenView->update();
388 const auto it = d->views.find(screen);
389 if (it != d->views.end()) {
393 for (
const auto &[screen, screenView] : d->views) {
406 return QVariantMap();
409void QuickSceneEffect::handleScreenAdded(
Output *screen)
414void QuickSceneEffect::handleScreenRemoved(Output *screen)
416 d->views.erase(screen);
417 d->incubators.erase(screen);
418 d->contexts.erase(screen);
421void QuickSceneEffect::addScreen(Output *screen)
424 properties[
"width"] = screen->geometry().width();
425 properties[
"height"] = screen->geometry().height();
427 auto incubator =
new QuickSceneViewIncubator(
this, screen, [
this, screen](QuickSceneViewIncubator *incubator) {
428 if (incubator->isReady()) {
429 auto view = incubator->result();
430 if (view->contentItem()) {
431 view->contentItem()->setFocus(false);
435 view->scheduleRepaint();
437 QJSEngine::setObjectOwnership(view.get(), QJSEngine::CppOwnership);
438 d->views[screen] = std::move(view);
439 }
else if (incubator->isError()) {
440 qCWarning(LIBKWINEFFECTS) <<
"Could not create a view for QML file" << d->delegate->url();
441 qCWarning(LIBKWINEFFECTS) << incubator->errors();
444 incubator->setInitialProperties(properties);
446 QQmlContext *parentContext;
447 if (QQmlContext *context = d->delegate->creationContext()) {
448 parentContext = context;
449 }
else if (QQmlContext *context = qmlContext(
this)) {
450 parentContext = context;
452 parentContext = d->delegate->engine()->rootContext();
454 QQmlContext *context =
new QQmlContext(parentContext);
456 d->contexts[screen].reset(context);
457 d->incubators[screen].reset(incubator);
458 d->delegate->create(*incubator, context);
461void QuickSceneEffect::startInternal()
468 if (Q_UNLIKELY(d->source.isEmpty())) {
469 qWarning() <<
"QuickSceneEffect.source is empty. Did you forget to call setSource()?";
474 d->delegate->loadUrl(d->source);
475 if (d->delegate->isError()) {
476 qWarning().nospace() <<
"Failed to load " << d->source <<
": " << d->delegate->errors();
480 Q_EMIT delegateChanged();
483 if (!d->delegate->isReady()) {
491 qApp->installEventFilter(
this);
494 for (Output *screen : screens) {
499 activateView(activeView());
501 connect(effects, &EffectsHandler::screenAdded,
this, &QuickSceneEffect::handleScreenAdded);
502 connect(effects, &EffectsHandler::screenRemoved,
this, &QuickSceneEffect::handleScreenRemoved);
508void QuickSceneEffect::stopInternal()
510 disconnect(effects, &EffectsHandler::screenAdded,
this, &QuickSceneEffect::handleScreenAdded);
511 disconnect(effects, &EffectsHandler::screenRemoved,
this, &QuickSceneEffect::handleScreenRemoved);
513 d->incubators.clear();
517 qApp->removeEventFilter(
this);
524void QuickSceneEffect::windowInputMouseEvent(QEvent *event)
526 Qt::MouseButtons buttons;
527 QPoint globalPosition;
528 if (QMouseEvent *mouseEvent =
dynamic_cast<QMouseEvent *
>(event)) {
529 buttons = mouseEvent->buttons();
530 globalPosition = mouseEvent->globalPos();
531 }
else if (QWheelEvent *wheelEvent =
dynamic_cast<QWheelEvent *
>(event)) {
532 buttons = wheelEvent->buttons();
533 globalPosition = wheelEvent->globalPosition().toPoint();
539 if (!d->mouseImplicitGrab) {
540 d->mouseImplicitGrab = viewAt(globalPosition);
546 target = viewAt(globalPosition);
550 d->mouseImplicitGrab =
nullptr;
555 activateView(target);
561void QuickSceneEffect::grabbedKeyboardEvent(QKeyEvent *keyEvent)
563 auto *screenView = activeView();
567 activateView(screenView);
568 screenView->forwardKeyEvent(keyEvent);
572bool QuickSceneEffect::touchDown(qint32
id,
const QPointF &pos, std::chrono::microseconds time)
574 for (
const auto &[screen, screenView] : d->views) {
575 if (screenView->geometry().contains(pos.toPoint())) {
576 activateView(screenView.get());
577 return screenView->forwardTouchDown(
id, pos, time);
583bool QuickSceneEffect::touchMotion(qint32
id,
const QPointF &pos, std::chrono::microseconds time)
585 for (
const auto &[screen, screenView] : d->views) {
586 if (screenView->geometry().contains(pos.toPoint())) {
587 return screenView->forwardTouchMotion(
id, pos, time);
593bool QuickSceneEffect::touchUp(qint32
id, std::chrono::microseconds time)
595 for (
const auto &[screen, screenView] : d->views) {
596 if (screenView->forwardTouchUp(
id, time)) {
605#include "moc_quickeffect.cpp"
Base class for all KWin effects.
Display * waylandDisplay() const
void stopMouseInterception(Effect *effect)
QQmlEngine * qmlEngine() const
Q_SCRIPTABLE void addRepaint(const QRectF &r)
CompositingType compositingType
virtual void defineCursor(Qt::CursorShape shape)
void setActiveFullScreenEffect(Effect *e)
void startMouseInterception(Effect *effect, Qt::CursorShape shape)
bool isScreenLocked() const
bool grabKeyboard(Effect *effect)
QList< Output * > screens() const
void renderOffscreenQuickView(const RenderTarget &renderTarget, const RenderViewport &viewport, OffscreenQuickView *effectQuickView) const
KWin::Output * activeScreen
Q_SCRIPTABLE void addRepaintFull()
Effect * activeFullScreenEffect() const
The KwinQuickView class provides a convenient API for exporting QtQuick scenes as buffers that can be...
QQuickWindow * window() const
QQuickItem * contentItem() const
void setGeometry(const QRect &rect)
void forwardMouseEvent(QEvent *mouseEvent)
void paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const QRegion ®ion, Output *screen) override
void prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime) override
QuickSceneEffect(QObject *parent=nullptr)
QuickSceneView * activeView
bool eventFilter(QObject *watched, QEvent *event) override
Q_INVOKABLE KWin::QuickSceneView * getView(Qt::Edge edge)
Q_INVOKABLE void activateView(QuickSceneView *view)
void setDelegate(QQmlComponent *delegate)
Q_INVOKABLE QuickSceneView * viewAt(const QPoint &pos) const
~QuickSceneEffect() override
bool isActive() const override
Q_INVOKABLE void checkItemDraggedOutOfScreen(QQuickItem *item)
void itemDraggedOutOfScreen(QQuickItem *item, QList< Output * > screens)
void itemDroppedOutOfScreen(const QPointF &globalPos, QQuickItem *item, Output *screen)
void activeViewChanged(KWin::QuickSceneView *view)
virtual QVariantMap initialProperties(Output *screen)
Q_INVOKABLE QuickSceneView * viewForScreen(Output *screen) const
Q_INVOKABLE void checkItemDroppedOutOfScreen(const QPointF &globalPos, QQuickItem *item)
void setSource(const QUrl &url)
void setRunning(bool running)
QPointer< QuickSceneView > mouseImplicitGrab
std::unique_ptr< QQmlComponent > delegate
std::map< Output *, std::unique_ptr< QuickSceneView > > views
std::map< Output *, std::unique_ptr< QQmlContext > > contexts
std::map< Output *, std::unique_ptr< QQmlIncubator > > incubators
bool isItemOnScreen(QQuickItem *item, Output *screen) const
static QuickSceneEffectPrivate * get(QuickSceneEffect *effect)
void setRootItem(QQuickItem *item)
QuickSceneEffect * effect
static QuickSceneView * findView(QQuickItem *item)
~QuickSceneView() override
static QuickSceneView * qmlAttachedProperties(QObject *object)
QuickSceneView(QuickSceneEffect *effect, Output *screen)
QuickSceneViewIncubator(QuickSceneEffect *effect, Output *screen, const std::function< void(QuickSceneViewIncubator *)> &statusChangedCallback)
std::unique_ptr< QuickSceneView > result()
void statusChanged(QQmlIncubator::Status status) override
void setInitialState(QObject *object) override