Add support for Drag'n'Drop on Wayland
Drag'n'Drop on Wayland allows us to improve the drag'n'drop experience. When entering a window during the drag'n'drop operation, KWin raises it. BUG: 36065 FIXED-IN: 5.6.0 (Wayland only)
This commit is contained in:
parent
9472756205
commit
8a1f19b145
3 changed files with 164 additions and 0 deletions
42
input.cpp
42
input.cpp
|
@ -687,6 +687,47 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class DragAndDropInputFilter : public InputEventFilter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool pointerEvent(QMouseEvent *event, quint32 nativeButton) override {
|
||||||
|
auto seat = waylandServer()->seat();
|
||||||
|
if (!seat->isDragPointer()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
seat->setTimestamp(event->timestamp());
|
||||||
|
switch (event->type()) {
|
||||||
|
case QEvent::MouseMove: {
|
||||||
|
if (Toplevel *t = input()->findToplevel(event->globalPos())) {
|
||||||
|
// TODO: consider decorations
|
||||||
|
if (t->surface() != seat->dragSurface()) {
|
||||||
|
if (AbstractClient *c = qobject_cast<AbstractClient*>(t)) {
|
||||||
|
workspace()->raiseClient(c);
|
||||||
|
}
|
||||||
|
seat->setPointerPos(event->globalPos());
|
||||||
|
seat->setDragTarget(t->surface(), event->globalPos(), t->inputTransformation());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// no window at that place, if we have a surface we need to reset
|
||||||
|
seat->setDragTarget(nullptr);
|
||||||
|
}
|
||||||
|
seat->setPointerPos(event->globalPos());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case QEvent::MouseButtonPress:
|
||||||
|
seat->pointerButtonPressed(nativeButton);
|
||||||
|
break;
|
||||||
|
case QEvent::MouseButtonRelease:
|
||||||
|
seat->pointerButtonReleased(nativeButton);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// TODO: should we pass through effects?
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
KWIN_SINGLETON_FACTORY(InputRedirection)
|
KWIN_SINGLETON_FACTORY(InputRedirection)
|
||||||
|
|
||||||
InputRedirection::InputRedirection(QObject *parent)
|
InputRedirection::InputRedirection(QObject *parent)
|
||||||
|
@ -808,6 +849,7 @@ void InputRedirection::setupInputFilters()
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (waylandServer()) {
|
if (waylandServer()) {
|
||||||
|
installInputEventFilter(new DragAndDropInputFilter);
|
||||||
installInputEventFilter(new LockScreenFilter);
|
installInputEventFilter(new LockScreenFilter);
|
||||||
}
|
}
|
||||||
installInputEventFilter(new ScreenEdgeInputFilter);
|
installInputEventFilter(new ScreenEdgeInputFilter);
|
||||||
|
|
|
@ -32,6 +32,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#include <KWayland/Client/connection_thread.h>
|
#include <KWayland/Client/connection_thread.h>
|
||||||
#include <KWayland/Client/buffer.h>
|
#include <KWayland/Client/buffer.h>
|
||||||
#include <KWayland/Server/buffer_interface.h>
|
#include <KWayland/Server/buffer_interface.h>
|
||||||
|
#include <KWayland/Server/datadevice_interface.h>
|
||||||
#include <KWayland/Server/display.h>
|
#include <KWayland/Server/display.h>
|
||||||
#include <KWayland/Server/seat_interface.h>
|
#include <KWayland/Server/seat_interface.h>
|
||||||
#include <KWayland/Server/surface_interface.h>
|
#include <KWayland/Server/surface_interface.h>
|
||||||
|
@ -128,6 +129,14 @@ void PointerInputRedirection::init()
|
||||||
connect(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::lockStateChanged, this, &PointerInputRedirection::update);
|
connect(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::lockStateChanged, this, &PointerInputRedirection::update);
|
||||||
connect(workspace(), &QObject::destroyed, this, [this] { m_inited = false; });
|
connect(workspace(), &QObject::destroyed, this, [this] { m_inited = false; });
|
||||||
connect(waylandServer(), &QObject::destroyed, this, [this] { m_inited = false; });
|
connect(waylandServer(), &QObject::destroyed, this, [this] { m_inited = false; });
|
||||||
|
connect(waylandServer()->seat(), &KWayland::Server::SeatInterface::dragEnded, this,
|
||||||
|
[this] {
|
||||||
|
// need to force a focused pointer change
|
||||||
|
waylandServer()->seat()->setFocusedPointerSurface(nullptr);
|
||||||
|
m_window.clear();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// warp the cursor to center of screen
|
// warp the cursor to center of screen
|
||||||
warp(screens()->geometry().center());
|
warp(screens()->geometry().center());
|
||||||
|
@ -216,6 +225,10 @@ void PointerInputRedirection::update()
|
||||||
if (!m_inited) {
|
if (!m_inited) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (waylandServer()->seat()->isDragPointer()) {
|
||||||
|
// ignore during drag and drop
|
||||||
|
return;
|
||||||
|
}
|
||||||
// TODO: handle pointer grab aka popups
|
// TODO: handle pointer grab aka popups
|
||||||
Toplevel *t = m_input->findToplevel(m_pos.toPoint());
|
Toplevel *t = m_input->findToplevel(m_pos.toPoint());
|
||||||
const auto oldDeco = m_decoration;
|
const auto oldDeco = m_decoration;
|
||||||
|
@ -507,6 +520,13 @@ CursorImage::CursorImage(PointerInputRedirection *parent)
|
||||||
, m_pointer(parent)
|
, m_pointer(parent)
|
||||||
{
|
{
|
||||||
connect(waylandServer()->seat(), &KWayland::Server::SeatInterface::focusedPointerChanged, this, &CursorImage::update);
|
connect(waylandServer()->seat(), &KWayland::Server::SeatInterface::focusedPointerChanged, this, &CursorImage::update);
|
||||||
|
connect(waylandServer()->seat(), &KWayland::Server::SeatInterface::dragStarted, this, &CursorImage::updateDrag);
|
||||||
|
connect(waylandServer()->seat(), &KWayland::Server::SeatInterface::dragEnded, this,
|
||||||
|
[this] {
|
||||||
|
disconnect(m_drag.connection);
|
||||||
|
reevaluteSource();
|
||||||
|
}
|
||||||
|
);
|
||||||
connect(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::lockStateChanged, this, &CursorImage::reevaluteSource);
|
connect(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::lockStateChanged, this, &CursorImage::reevaluteSource);
|
||||||
connect(m_pointer, &PointerInputRedirection::decorationChanged, this, &CursorImage::updateDecoration);
|
connect(m_pointer, &PointerInputRedirection::decorationChanged, this, &CursorImage::updateDecoration);
|
||||||
// connect the move resize of all window
|
// connect the move resize of all window
|
||||||
|
@ -536,6 +556,28 @@ CursorImage::~CursorImage() = default;
|
||||||
|
|
||||||
void CursorImage::markAsRendered()
|
void CursorImage::markAsRendered()
|
||||||
{
|
{
|
||||||
|
if (m_currentSource == CursorSource::DragAndDrop) {
|
||||||
|
// always sending a frame rendered to the drag icon surface to not freeze QtWayland (see https://bugreports.qt.io/browse/QTBUG-51599 )
|
||||||
|
if (auto ddi = waylandServer()->seat()->dragSource()) {
|
||||||
|
if (auto s = ddi->icon()) {
|
||||||
|
s->frameRendered(m_surfaceRenderedTimer.elapsed());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto p = waylandServer()->seat()->dragPointer();
|
||||||
|
if (!p) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto c = p->cursor();
|
||||||
|
if (!c) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto cursorSurface = c->surface();
|
||||||
|
if (cursorSurface.isNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cursorSurface->frameRendered(m_surfaceRenderedTimer.elapsed());
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (m_currentSource != CursorSource::LockScreen && m_currentSource != CursorSource::PointerSurface) {
|
if (m_currentSource != CursorSource::LockScreen && m_currentSource != CursorSource::PointerSurface) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -680,6 +722,70 @@ void CursorImage::removeEffectsOverrideCursor()
|
||||||
reevaluteSource();
|
reevaluteSource();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CursorImage::updateDrag()
|
||||||
|
{
|
||||||
|
using namespace KWayland::Server;
|
||||||
|
disconnect(m_drag.connection);
|
||||||
|
m_drag.cursor.image = QImage();
|
||||||
|
m_drag.cursor.hotSpot = QPoint();
|
||||||
|
reevaluteSource();
|
||||||
|
if (auto p = waylandServer()->seat()->dragPointer()) {
|
||||||
|
m_drag.connection = connect(p, &PointerInterface::cursorChanged, this, &CursorImage::updateDragCursor);
|
||||||
|
} else {
|
||||||
|
m_drag.connection = QMetaObject::Connection();
|
||||||
|
}
|
||||||
|
updateDragCursor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CursorImage::updateDragCursor()
|
||||||
|
{
|
||||||
|
m_drag.cursor.image = QImage();
|
||||||
|
m_drag.cursor.hotSpot = QPoint();
|
||||||
|
const bool needsEmit = m_currentSource == CursorSource::DragAndDrop;
|
||||||
|
QImage additionalIcon;
|
||||||
|
if (auto ddi = waylandServer()->seat()->dragSource()) {
|
||||||
|
if (auto dragIcon = ddi->icon()) {
|
||||||
|
if (auto buffer = dragIcon->buffer()) {
|
||||||
|
additionalIcon = buffer->data().copy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto p = waylandServer()->seat()->dragPointer();
|
||||||
|
if (!p) {
|
||||||
|
if (needsEmit) {
|
||||||
|
emit changed();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto c = p->cursor();
|
||||||
|
if (!c) {
|
||||||
|
if (needsEmit) {
|
||||||
|
emit changed();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto cursorSurface = c->surface();
|
||||||
|
if (cursorSurface.isNull()) {
|
||||||
|
if (needsEmit) {
|
||||||
|
emit changed();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto buffer = cursorSurface.data()->buffer();
|
||||||
|
if (!buffer) {
|
||||||
|
if (needsEmit) {
|
||||||
|
emit changed();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_drag.cursor.hotSpot = c->hotspot();
|
||||||
|
m_drag.cursor.image = buffer->data().copy();
|
||||||
|
if (needsEmit) {
|
||||||
|
emit changed();
|
||||||
|
}
|
||||||
|
// TODO: add the cursor image
|
||||||
|
}
|
||||||
|
|
||||||
void CursorImage::loadThemeCursor(Qt::CursorShape shape, Image *image)
|
void CursorImage::loadThemeCursor(Qt::CursorShape shape, Image *image)
|
||||||
{
|
{
|
||||||
loadTheme();
|
loadTheme();
|
||||||
|
@ -712,6 +818,11 @@ void CursorImage::loadThemeCursor(Qt::CursorShape shape, Image *image)
|
||||||
|
|
||||||
void CursorImage::reevaluteSource()
|
void CursorImage::reevaluteSource()
|
||||||
{
|
{
|
||||||
|
if (waylandServer()->seat()->isDragPointer()) {
|
||||||
|
// TODO: touch drag?
|
||||||
|
setSource(CursorSource::DragAndDrop);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (waylandServer()->isScreenLocked()) {
|
if (waylandServer()->isScreenLocked()) {
|
||||||
setSource(CursorSource::LockScreen);
|
setSource(CursorSource::LockScreen);
|
||||||
return;
|
return;
|
||||||
|
@ -757,6 +868,8 @@ QImage CursorImage::image() const
|
||||||
return m_serverCursor.image;
|
return m_serverCursor.image;
|
||||||
case CursorSource::Decoration:
|
case CursorSource::Decoration:
|
||||||
return m_decorationCursor.image;
|
return m_decorationCursor.image;
|
||||||
|
case CursorSource::DragAndDrop:
|
||||||
|
return m_drag.cursor.image;
|
||||||
case CursorSource::Fallback:
|
case CursorSource::Fallback:
|
||||||
return m_fallbackCursor.image;
|
return m_fallbackCursor.image;
|
||||||
default:
|
default:
|
||||||
|
@ -777,6 +890,8 @@ QPoint CursorImage::hotSpot() const
|
||||||
return m_serverCursor.hotSpot;
|
return m_serverCursor.hotSpot;
|
||||||
case CursorSource::Decoration:
|
case CursorSource::Decoration:
|
||||||
return m_decorationCursor.hotSpot;
|
return m_decorationCursor.hotSpot;
|
||||||
|
case CursorSource::DragAndDrop:
|
||||||
|
return m_drag.cursor.hotSpot;
|
||||||
case CursorSource::Fallback:
|
case CursorSource::Fallback:
|
||||||
return m_fallbackCursor.hotSpot;
|
return m_fallbackCursor.hotSpot;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -144,6 +144,8 @@ private:
|
||||||
void updateDecoration();
|
void updateDecoration();
|
||||||
void updateDecorationCursor();
|
void updateDecorationCursor();
|
||||||
void updateMoveResize();
|
void updateMoveResize();
|
||||||
|
void updateDrag();
|
||||||
|
void updateDragCursor();
|
||||||
void loadTheme();
|
void loadTheme();
|
||||||
struct Image {
|
struct Image {
|
||||||
QImage image;
|
QImage image;
|
||||||
|
@ -157,6 +159,7 @@ private:
|
||||||
MoveResize,
|
MoveResize,
|
||||||
PointerSurface,
|
PointerSurface,
|
||||||
Decoration,
|
Decoration,
|
||||||
|
DragAndDrop,
|
||||||
Fallback
|
Fallback
|
||||||
};
|
};
|
||||||
void setSource(CursorSource source);
|
void setSource(CursorSource source);
|
||||||
|
@ -177,6 +180,10 @@ private:
|
||||||
Image m_moveResizeCursor;
|
Image m_moveResizeCursor;
|
||||||
QHash<Qt::CursorShape, Image> m_cursors;
|
QHash<Qt::CursorShape, Image> m_cursors;
|
||||||
QElapsedTimer m_surfaceRenderedTimer;
|
QElapsedTimer m_surfaceRenderedTimer;
|
||||||
|
struct {
|
||||||
|
Image cursor;
|
||||||
|
QMetaObject::Connection connection;
|
||||||
|
} m_drag;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue