1
0
mirror of https://github.com/KDE/latte-dock.git synced 2025-02-01 01:47:23 +03:00
latte-dock/app/wm/waylandinterface.cpp

473 lines
13 KiB
C++
Raw Normal View History

/*
* Copyright 2016 Smith AR <audoban@openmailbox.org>
* Michail Vourlakos <mvourlakos@gmail.com>
*
* This file is part of Latte-Dock
*
* Latte-Dock is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* Latte-Dock is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "waylandinterface.h"
2018-12-02 02:05:52 +02:00
// local
#include "view/screenedgeghostwindow.h"
#include "view/view.h"
#include "../lattecorona.h"
#include "../liblatte2/extras.h"
2018-12-02 02:05:52 +02:00
// Qt
#include <QDebug>
#include <QTimer>
2017-06-10 16:33:46 -05:00
#include <QApplication>
#include <QSignalMapper>
#include <QtX11Extras/QX11Info>
2017-06-29 15:33:12 -05:00
#include <QRasterWindow>
2018-12-02 02:05:52 +02:00
// KDE
#include <KWindowSystem>
#include <KWindowInfo>
#include <KWayland/Client/surface.h>
2017-06-29 15:33:12 -05:00
2018-12-02 02:05:52 +02:00
// X11
#include <NETWM>
2017-06-10 16:33:46 -05:00
using namespace KWayland::Client;
namespace Latte {
2018-07-03 22:15:45 +03:00
class Private::GhostWindow : public QRasterWindow
{
2017-06-29 15:33:12 -05:00
Q_OBJECT
public:
GhostWindow(WaylandInterface *waylandInterface)
: m_waylandInterface(waylandInterface) {
2017-06-29 15:33:12 -05:00
setFlags(Qt::FramelessWindowHint
| Qt::WindowStaysOnTopHint
| Qt::NoDropShadowWindowHint
| Qt::WindowDoesNotAcceptFocus);
2017-06-29 15:33:12 -05:00
setupWaylandIntegration();
show();
}
~GhostWindow() {
2017-06-29 15:33:12 -05:00
delete m_shellSurface;
}
void setGeometry(const QRect &rect) {
2017-06-29 15:33:12 -05:00
QWindow::setGeometry(rect);
setMaximumSize(rect.size());
2017-06-29 15:33:12 -05:00
m_shellSurface->setPosition(rect.topLeft());
}
void setupWaylandIntegration() {
2017-06-29 15:33:12 -05:00
using namespace KWayland::Client;
if (m_shellSurface)
return;
Surface *s{Surface::fromWindow(this)};
if (!s)
return;
m_shellSurface = m_waylandInterface->waylandCoronaInterface()->createSurface(s, this);
2017-06-29 15:33:12 -05:00
qDebug() << "wayland ghost window surface was created...";
m_shellSurface->setSkipTaskbar(true);
m_shellSurface->setPanelTakesFocus(false);
m_shellSurface->setRole(PlasmaShellSurface::Role::Panel);
m_shellSurface->setPanelBehavior(PlasmaShellSurface::PanelBehavior::AlwaysVisible);
}
KWayland::Client::PlasmaShellSurface *m_shellSurface{nullptr};
WaylandInterface *m_waylandInterface{nullptr};
2017-06-29 15:33:12 -05:00
};
WaylandInterface::WaylandInterface(QObject *parent)
: AbstractWindowInterface(parent)
{
m_corona = qobject_cast<Latte::Corona *>(parent);
2017-06-10 16:33:46 -05:00
m_activities = new KActivities::Consumer(this);
2017-06-10 16:33:46 -05:00
connect(m_activities.data(), &KActivities::Consumer::currentActivityChanged
, this, &WaylandInterface::currentActivityChanged);
}
WaylandInterface::~WaylandInterface()
{
}
2017-06-10 16:33:46 -05:00
void WaylandInterface::init()
{
}
void WaylandInterface::initWindowManagement(KWayland::Client::PlasmaWindowManagement *windowManagement)
{
m_windowManagement = windowManagement;
connect(m_windowManagement, &PlasmaWindowManagement::windowCreated, this, &WaylandInterface::windowCreatedProxy);
connect(m_windowManagement, &PlasmaWindowManagement::activeWindowChanged, this, [&]() noexcept {
auto w = m_windowManagement->activeWindow();
if (!w || (w && (!isPlasmaDesktop(w) && w->appId() != QLatin1String("latte-dock")))) {
2019-02-11 18:40:13 +02:00
emit activeWindowChanged(w ? w->internalId() : 0);
}
}, Qt::QueuedConnection);
}
KWayland::Client::PlasmaShell *WaylandInterface::waylandCoronaInterface() const
{
return m_corona->waylandCoronaInterface();
}
2018-12-06 16:09:42 +02:00
void WaylandInterface::setViewExtraFlags(QWindow &view)
{
Q_UNUSED(view)
}
2018-12-06 16:09:42 +02:00
void WaylandInterface::setViewStruts(QWindow &view, const QRect &rect, Plasma::Types::Location location)
{
2017-06-29 15:33:12 -05:00
if (!m_ghostWindows.contains(view.winId()))
m_ghostWindows[view.winId()] = new Private::GhostWindow(this);
auto w = m_ghostWindows[view.winId()];
switch (location) {
2017-06-29 15:33:12 -05:00
case Plasma::Types::TopEdge:
case Plasma::Types::BottomEdge:
w->setGeometry({rect.x() + rect.width() / 2, rect.y(), 1, rect.height()});
break;
case Plasma::Types::LeftEdge:
case Plasma::Types::RightEdge:
w->setGeometry({rect.x(), rect.y() + rect.height() / 2, rect.width(), 1});
break;
default:
break;
}
}
void WaylandInterface::setWindowOnActivities(QWindow &window, const QStringList &activities)
{
//! needs to updated to wayland case
// KWindowSystem::setOnActivities(view.winId(), activities);
}
2018-12-06 16:09:42 +02:00
void WaylandInterface::removeViewStruts(QWindow &view) const
{
2017-06-29 15:33:12 -05:00
delete m_ghostWindows.take(view.winId());
}
WindowId WaylandInterface::activeWindow() const
{
if (!m_windowManagement) {
return 0;
}
auto wid = m_windowManagement->activeWindow();
2017-06-10 16:33:46 -05:00
return wid ? wid->internalId() : 0;
}
const std::list<WindowId> &WaylandInterface::windows() const
{
return m_windows;
}
void WaylandInterface::setKeepAbove(const QDialog &dialog, bool above) const
{
if (above) {
KWindowSystem::setState(dialog.winId(), NET::KeepAbove);
} else {
KWindowSystem::clearState(dialog.winId(), NET::KeepAbove);
}
}
void WaylandInterface::skipTaskBar(const QDialog &dialog) const
{
KWindowSystem::setState(dialog.winId(), NET::SkipTaskbar);
}
void WaylandInterface::slideWindow(QWindow &view, AbstractWindowInterface::Slide location) const
{
auto slideLocation = KWindowEffects::NoEdge;
switch (location) {
case Slide::Top:
slideLocation = KWindowEffects::TopEdge;
break;
case Slide::Bottom:
slideLocation = KWindowEffects::BottomEdge;
break;
case Slide::Left:
slideLocation = KWindowEffects::LeftEdge;
break;
case Slide::Right:
slideLocation = KWindowEffects::RightEdge;
break;
default:
break;
}
KWindowEffects::slideWindow(view.winId(), slideLocation, -1);
}
void WaylandInterface::enableBlurBehind(QWindow &view) const
{
KWindowEffects::enableBlurBehind(view.winId());
}
void WaylandInterface::setEdgeStateFor(QWindow *view, bool active) const
{
ViewPart::ScreenEdgeGhostWindow *window = qobject_cast<ViewPart::ScreenEdgeGhostWindow *>(view);
if (!window) {
return;
}
2018-12-06 16:09:42 +02:00
if (window->parentView()->surface() && window->parentView()->visibility()
&& (window->parentView()->visibility()->mode() == Types::DodgeActive
|| window->parentView()->visibility()->mode() == Types::DodgeMaximized
|| window->parentView()->visibility()->mode() == Types::DodgeAllWindows
|| window->parentView()->visibility()->mode() == Types::AutoHide)) {
if (active) {
window->showWithMask();
window->surface()->requestHideAutoHidingPanel();
} else {
window->hideWithMask();
window->surface()->requestShowAutoHidingPanel();
}
}
}
WindowInfoWrap WaylandInterface::requestInfoActive() const
{
if (!m_windowManagement) {
return {};
}
auto w = m_windowManagement->activeWindow();
2017-06-10 16:33:46 -05:00
if (!w) return {};
WindowInfoWrap winfoWrap;
2017-06-10 16:33:46 -05:00
winfoWrap.setIsValid(true);
2017-06-10 16:33:46 -05:00
winfoWrap.setWid(w->internalId());
2017-06-10 16:33:46 -05:00
winfoWrap.setIsActive(w->isActive());
2017-06-14 12:50:17 -05:00
winfoWrap.setIsMinimized(w->isMinimized());
2017-06-10 16:33:46 -05:00
winfoWrap.setIsMaxVert(w->isMaximized());
2017-06-10 16:33:46 -05:00
winfoWrap.setIsMaxHoriz(w->isMaximized());
2017-06-10 16:33:46 -05:00
winfoWrap.setIsFullscreen(w->isFullscreen());
2017-06-10 16:33:46 -05:00
winfoWrap.setIsShaded(w->isShaded());
2017-06-14 12:50:17 -05:00
winfoWrap.setGeometry(w->geometry());
2017-06-10 16:33:46 -05:00
winfoWrap.setIsKeepAbove(w->isKeepAbove());
winfoWrap.setHasSkipTaskbar(w->skipTaskbar());
2017-06-10 16:33:46 -05:00
return winfoWrap;
}
bool WaylandInterface::isOnCurrentDesktop(WindowId wid) const
{
if (!m_windowManagement) {
return false;
}
auto it = std::find_if(m_windowManagement->windows().constBegin(), m_windowManagement->windows().constEnd(), [&wid](PlasmaWindow * w) noexcept {
2017-06-10 16:33:46 -05:00
return w->isValid() && w->internalId() == wid;
});
//qDebug() << "desktop:" << (it != m_windowManagement->windows().constEnd() ? (*it)->virtualDesktop() : -1) << KWindowSystem::currentDesktop();
2017-06-14 14:45:43 -05:00
//return true;
return it != m_windowManagement->windows().constEnd() && ((*it)->virtualDesktop() == KWindowSystem::currentDesktop() || (*it)->isOnAllDesktops());
}
2017-06-20 19:07:47 -05:00
bool WaylandInterface::isOnCurrentActivity(WindowId wid) const
{
auto it = std::find_if(m_windowManagement->windows().constBegin(), m_windowManagement->windows().constEnd(), [&wid](PlasmaWindow * w) noexcept {
2017-06-20 19:07:47 -05:00
return w->isValid() && w->internalId() == wid;
});
//TODO: Not yet implemented
return it != m_windowManagement->windows().constEnd() && true;
2017-06-20 19:07:47 -05:00
}
WindowInfoWrap WaylandInterface::requestInfo(WindowId wid) const
{
WindowInfoWrap winfoWrap;
auto w = windowFor(wid);
if (w) {
if (isPlasmaDesktop(w)) {
winfoWrap.setIsValid(true);
winfoWrap.setIsPlasmaDesktop(true);
winfoWrap.setWid(wid);
} else if (isValidWindow(w)) {
winfoWrap.setIsValid(true);
winfoWrap.setWid(wid);
winfoWrap.setIsActive(w->isActive());
winfoWrap.setIsMinimized(w->isMinimized());
winfoWrap.setIsMaxVert(w->isMaximized());
winfoWrap.setIsMaxHoriz(w->isMaximized());
winfoWrap.setIsFullscreen(w->isFullscreen());
winfoWrap.setIsShaded(w->isShaded());
winfoWrap.setGeometry(w->geometry());
winfoWrap.setHasSkipTaskbar(w->skipTaskbar());
}
} else {
return {};
}
return winfoWrap;
}
KWayland::Client::PlasmaWindow *WaylandInterface::windowFor(WindowId wid) const
{
auto it = std::find_if(m_windowManagement->windows().constBegin(), m_windowManagement->windows().constEnd(), [&wid](PlasmaWindow * w) noexcept {
return w->isValid() && w->internalId() == wid;
});
if (it == m_windowManagement->windows().constEnd()) {
return nullptr;
2017-06-10 16:33:46 -05:00
}
return *it;
}
bool WaylandInterface::windowCanBeDragged(WindowId wid) const
{
WindowInfoWrap winfo = requestInfo(wid);
return (winfo.isValid() && !winfo.isPlasmaDesktop() && !winfo.hasSkipTaskbar());
}
void WaylandInterface::releaseMouseEventFor(WindowId wid) const
{
// this isnt really needed under wayland
}
void WaylandInterface::requestMoveWindow(WindowId wid, QPoint from) const
{
if (windowCanBeDragged(wid)) {
auto w = windowFor(wid);
if (w && isValidWindow(w)) {
w->requestMove();
}
}
}
void WaylandInterface::requestToggleMaximized(WindowId wid) const
{
auto w = windowFor(wid);
if (w && isValidWindow(w)) {
w->requestToggleMaximized();
}
}
bool WaylandInterface::isPlasmaDesktop(const KWayland::Client::PlasmaWindow *w) const
{
if (!w || (w->appId() != QLatin1String("org.kde.plasmashell"))) {
return false;
}
bool hasScreenGeometry{false};
foreach(auto scr, qGuiApp->screens()) {
if (!w->geometry().isEmpty() && w->geometry() == scr->geometry()) {
hasScreenGeometry = true;
break;
}
}
return hasScreenGeometry;
}
bool WaylandInterface::isValidWindow(const KWayland::Client::PlasmaWindow *w) const
{
//! DEPRECATED comment is case we must reenable this
2018-08-31 16:49:47 +03:00
//! because wayland does not have any way yet to identify the window type
//! a trick is to just consider windows as valid when they can be shown in the
//! taskbar. Of course that creates issues with plasma native dialogs
//! e.g. widgets explorer, Activities etc. that are not used to hide
2018-12-06 16:09:42 +02:00
//! the dodge views appropriately
return w->isValid() && !isPlasmaDesktop(w) && w->appId()!=QLatin1String("latte-dock");// && !w->skipTaskbar();
}
2017-06-10 16:33:46 -05:00
void WaylandInterface::windowCreatedProxy(KWayland::Client::PlasmaWindow *w)
{
2017-06-10 16:33:46 -05:00
if (!isValidWindow(w)) return;
2017-06-10 16:33:46 -05:00
if (!mapper) mapper = new QSignalMapper(this);
2017-06-10 16:33:46 -05:00
mapper->setMapping(w, w);
connect(w, &PlasmaWindow::unmapped, this, [ &, win = w]() noexcept {
2017-06-10 16:33:46 -05:00
mapper->removeMappings(win);
m_windows.remove(win->internalId());
emit windowRemoved(win->internalId());
});
connect(w, SIGNAL(activeChanged()), mapper, SLOT(map()));
2017-06-10 16:33:46 -05:00
connect(w, SIGNAL(fullscreenChanged()), mapper, SLOT(map()));
connect(w, SIGNAL(geometryChanged()), mapper, SLOT(map()));
connect(w, SIGNAL(maximizedChanged()), mapper, SLOT(map()));
2017-06-14 14:45:43 -05:00
connect(w, SIGNAL(minimizedChanged()), mapper, SLOT(map()));
2017-06-10 16:33:46 -05:00
connect(w, SIGNAL(shadedChanged()), mapper, SLOT(map()));
2017-06-14 16:10:31 -05:00
connect(w, SIGNAL(skipTaskbarChanged()), mapper, SLOT(map()));
2017-06-14 14:45:43 -05:00
connect(w, SIGNAL(onAllDesktopsChanged()), mapper, SLOT(map()));
2017-06-10 16:33:46 -05:00
connect(w, SIGNAL(virtualDesktopChanged()), mapper, SLOT(map()));
connect(mapper, static_cast<void (QSignalMapper::*)(QObject *)>(&QSignalMapper::mapped)
, this, [&](QObject * w) noexcept {
2018-03-02 20:28:22 +02:00
//qDebug() << "window changed:" << qobject_cast<PlasmaWindow *>(w)->appId();
2019-02-11 18:40:13 +02:00
PlasmaWindow *pW = qobject_cast<PlasmaWindow*>(w);
if (pW && !isPlasmaDesktop(pW) && pW->appId() != QLatin1String("latte-dock")) {
2019-02-11 18:40:13 +02:00
emit windowChanged(pW->internalId());
}
2017-06-10 16:33:46 -05:00
});
2017-06-10 16:33:46 -05:00
m_windows.push_back(w->internalId());
2017-06-10 16:33:46 -05:00
emit windowAdded(w->internalId());
}
}
2017-06-29 15:34:36 -05:00
#include "waylandinterface.moc"