dde-dock/frame/item/components/appsnapshot.cpp
chenjun 76d0acd6d5 fix: 开始预览时获取所有应用窗口的最小化状态,退出预览时根据应用窗口的最小化状态设置窗口是否被最小化到任务栏
在预览应用时,由于预览会展开应用窗口,将应用由最小化还原为显示状态,而退出预览时又没有重新将应用窗口最小化,修改后在预览时,先获取应用窗口是否已
被最小化到任务栏,然后在退出预览时,根据是否最小化条件,将应用窗口最小化到任务栏

Log: 修复3D模式下任务栏,鼠标划过应用图标上方预览窗口时,应用显示在桌面,鼠标移开时,应用没有退回最小化状态问题
Bug: https://pms.uniontech.com/zentao/bug-view-42002.html
Change-Id: I60b5dbdbfc102d6541acc326f713fe2e220ba3fc
Reviewed-on: http://gerrit.uniontech.com/c/dde-dock/+/2854
Reviewed-by: <mailman@uniontech.com>
Reviewed-by: lizhongming <lizhongming@uniontech.com>
Tested-by: <mailman@uniontech.com>
2020-08-28 17:06:08 +08:00

397 lines
12 KiB
C++

/*
* Copyright (C) 2011 ~ 2018 Deepin Technology Co., Ltd.
*
* Author: sbw <sbw@sbw.so>
*
* Maintainer: sbw <sbw@sbw.so>
*
* This program 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 3 of the License, or
* any later version.
*
* This program 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 "appsnapshot.h"
#include "previewcontainer.h"
#include "../widgets/tipswidget.h"
#include <DStyle>
#include <X11/Xlib.h>
#include <X11/X.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <sys/shm.h>
#include <QX11Info>
#include <QPainter>
#include <QVBoxLayout>
#include <QSizeF>
#include <QTimer>
struct SHMInfo {
long shmid;
long width;
long height;
long bytesPerLine;
long format;
struct Rect {
long x;
long y;
long width;
long height;
} rect;
};
using namespace Dock;
AppSnapshot::AppSnapshot(const WId wid, QWidget *parent)
: QWidget(parent)
, m_wid(wid)
, m_isWidowHidden(false)
, m_title(new TipsWidget)
, m_waitLeaveTimer(new QTimer(this))
, m_closeBtn2D(new DIconButton(this))
, m_wmHelper(DWindowManagerHelper::instance())
, m_dockDaemonInter(new DockDaemonInter("com.deepin.dde.daemon.Dock", "/com/deepin/dde/daemon/Dock", QDBusConnection::sessionBus(), this))
{
m_closeBtn2D->setFixedSize(24, 24);
m_closeBtn2D->setIconSize(QSize(24, 24));
m_closeBtn2D->setObjectName("closebutton-2d");
m_closeBtn2D->setIcon(QIcon(":/icons/resources/close_round_normal.svg"));
m_closeBtn2D->setVisible(false);
m_closeBtn2D->setFlat(true);
m_closeBtn2D->installEventFilter(this);
m_title->setObjectName("AppSnapshotTitle");
QHBoxLayout *centralLayout = new QHBoxLayout;
centralLayout->addWidget(m_title);
centralLayout->setMargin(0);
setLayout(centralLayout);
setAcceptDrops(true);
resize(SNAP_WIDTH, SNAP_HEIGHT);
connect(m_closeBtn2D, &DIconButton::clicked, this, &AppSnapshot::closeWindow, Qt::QueuedConnection);
connect(m_wmHelper, &DWindowManagerHelper::hasCompositeChanged, this, &AppSnapshot::compositeChanged, Qt::QueuedConnection);
QTimer::singleShot(1, this, &AppSnapshot::compositeChanged);
}
void AppSnapshot::setWindowState()
{
if (m_isWidowHidden) {
m_dockDaemonInter->MinimizeWindow(m_wid);
}
}
void AppSnapshot::closeWindow() const
{
const auto display = QX11Info::display();
XEvent e;
memset(&e, 0, sizeof(e));
e.xclient.type = ClientMessage;
e.xclient.window = m_wid;
e.xclient.message_type = XInternAtom(display, "WM_PROTOCOLS", true);
e.xclient.format = 32;
e.xclient.data.l[0] = XInternAtom(display, "WM_DELETE_WINDOW", false);
e.xclient.data.l[1] = CurrentTime;
XSendEvent(display, m_wid, false, NoEventMask, &e);
XFlush(display);
}
void AppSnapshot::compositeChanged() const
{
const bool composite = m_wmHelper->hasComposite();
m_title->setVisible(!composite);
QTimer::singleShot(1, this, &AppSnapshot::fetchSnapshot);
}
void AppSnapshot::setWindowInfo(const WindowInfo &info)
{
m_windowInfo = info;
QFontMetrics fm(m_title->font());
QString strTtile = m_title->fontMetrics().elidedText(m_windowInfo.title, Qt::ElideRight, width());
m_title->setText(strTtile);
getWindowState();
}
void AppSnapshot::dragEnterEvent(QDragEnterEvent *e)
{
QWidget::dragEnterEvent(e);
if (m_wmHelper->hasComposite())
emit entered(m_wid);
}
void AppSnapshot::fetchSnapshot()
{
if (!m_wmHelper->hasComposite())
return;
QImage qimage;
SHMInfo *info = nullptr;
uchar *image_data = nullptr;
XImage *ximage = nullptr;
do {
// get window image from shm(only for deepin app)
info = getImageDSHM();
if (info) {
qDebug() << "get Image from dxcbplugin SHM...";
//qDebug() << info->shmid << info->width << info->height << info->bytesPerLine << info->format << info->rect.x << info->rect.y << info->rect.width << info->rect.height;
image_data = (uchar *)shmat(info->shmid, 0, 0);
if ((qint64)image_data != -1) {
m_snapshot = QImage(image_data, info->width, info->height, info->bytesPerLine, (QImage::Format)info->format);
m_snapshotSrcRect = QRect(info->rect.x, info->rect.y, info->rect.width, info->rect.height);
break;
}
qDebug() << "invalid pointer of shm!";
image_data = nullptr;
}
if (!image_data || qimage.isNull()) {
// get window image from XGetImage(a little slow)
qDebug() << "get Image from dxcbplugin SHM failed!";
qDebug() << "get Image from Xlib...";
ximage = getImageXlib();
if (!ximage) {
qDebug() << "get Image from Xlib failed! giving up...";
emit requestCheckWindow();
return;
}
qimage = QImage((const uchar *)(ximage->data), ximage->width, ximage->height, ximage->bytes_per_line, QImage::Format_RGB32);
}
Q_ASSERT(!qimage.isNull());
// remove shadow frame
m_snapshotSrcRect = rectRemovedShadow(qimage, nullptr);
m_snapshot = qimage;
} while (false);
QSizeF size(rect().marginsRemoved(QMargins(8, 8, 8, 8)).size());
const auto ratio = devicePixelRatioF();
size = m_snapshotSrcRect.size().scaled(size * ratio, Qt::KeepAspectRatio);
qreal scale = qreal(size.width()) / m_snapshotSrcRect.width();
m_snapshot = m_snapshot.scaled(qRound(m_snapshot.width() * scale), qRound(m_snapshot.height() * scale),
Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
m_snapshotSrcRect.moveTop(m_snapshotSrcRect.top() * scale + 0.5);
m_snapshotSrcRect.moveLeft(m_snapshotSrcRect.left() * scale + 0.5);
m_snapshotSrcRect.setWidth(size.width() - 0.5);
m_snapshotSrcRect.setHeight(size.height() - 0.5);
m_snapshot.setDevicePixelRatio(ratio);
if (image_data) shmdt(image_data);
if (ximage) XDestroyImage(ximage);
if (info) XFree(info);
update();
}
void AppSnapshot::enterEvent(QEvent *e)
{
QWidget::enterEvent(e);
if (!m_wmHelper->hasComposite()) {
m_closeBtn2D->move(width() - m_closeBtn2D->width() - 5, (height() - m_closeBtn2D->height()) / 2);
m_closeBtn2D->setVisible(true);
} else {
emit entered(wid());
}
update();
}
void AppSnapshot::leaveEvent(QEvent *e)
{
QWidget::leaveEvent(e);
m_closeBtn2D->setVisible(false);
update();
}
void AppSnapshot::paintEvent(QPaintEvent *e)
{
QPainter painter(this);
if (!m_wmHelper->hasComposite()) {
if (underMouse())
painter.fillRect(rect(), QColor(255, 255, 255, 255 * .2));
return;
}
if (m_snapshot.isNull())
return;
const auto ratio = devicePixelRatioF();
// draw attention background
if (m_windowInfo.attention) {
painter.setBrush(QColor(241, 138, 46, 255 * .8));
painter.setPen(Qt::NoPen);
painter.drawRoundedRect(rect(), 5, 5);
}
// draw image
const QImage &im = m_snapshot;
const qreal offset_x = width() / 2.0 - m_snapshotSrcRect.width() / ratio / 2 - m_snapshotSrcRect.left() / ratio;
const qreal offset_y = height() / 2.0 - m_snapshotSrcRect.height() / ratio / 2 - m_snapshotSrcRect.top() / ratio;
DStyleHelper dstyle(style());
const int radius = dstyle.pixelMetric(DStyle::PM_FrameRadius);
QBrush brush;
brush.setTextureImage(im);
painter.setBrush(brush);
painter.setPen(Qt::NoPen);
painter.scale(1 / ratio, 1 / ratio);
painter.translate(QPoint(offset_x * ratio, offset_y * ratio));
painter.drawRoundedRect(m_snapshotSrcRect, radius * ratio, radius * ratio);
}
void AppSnapshot::resizeEvent(QResizeEvent *e)
{
QWidget::resizeEvent(e);
QTimer::singleShot(1, this, &AppSnapshot::fetchSnapshot);
}
void AppSnapshot::mousePressEvent(QMouseEvent *e)
{
QWidget::mousePressEvent(e);
emit clicked(m_wid);
}
bool AppSnapshot::eventFilter(QObject *watched, QEvent *e)
{
if(watched == m_closeBtn2D) {
if(watched == m_closeBtn2D && (e->type() == QEvent::HoverEnter || e->type() == QEvent::HoverMove)) {
m_closeBtn2D->setIcon(QIcon(":/icons/resources/close_round_hover.svg"));
}
else if (watched == m_closeBtn2D && e->type() == QEvent::HoverLeave) {
m_closeBtn2D->setIcon(QIcon(":/icons/resources/close_round_normal.svg"));
}
else if (watched == m_closeBtn2D && e->type() == QEvent::MouseButtonPress) {
m_closeBtn2D->setIcon(QIcon(":/icons/resources/close_round_press.svg"));
}
}
return false;
}
SHMInfo *AppSnapshot::getImageDSHM()
{
const auto display = QX11Info::display();
Atom atom_prop = XInternAtom(display, "_DEEPIN_DXCB_SHM_INFO", true);
if (!atom_prop) {
return nullptr;
}
Atom actual_type_return_deepin_shm;
int actual_format_return_deepin_shm;
unsigned long nitems_return_deepin_shm;
unsigned long bytes_after_return_deepin_shm;
unsigned char *prop_return_deepin_shm;
XGetWindowProperty(display, m_wid, atom_prop, 0, 32 * 9, false, AnyPropertyType,
&actual_type_return_deepin_shm, &actual_format_return_deepin_shm, &nitems_return_deepin_shm,
&bytes_after_return_deepin_shm, &prop_return_deepin_shm);
//qDebug() << actual_type_return_deepin_shm << actual_format_return_deepin_shm << nitems_return_deepin_shm << bytes_after_return_deepin_shm << prop_return_deepin_shm;
return reinterpret_cast<SHMInfo *>(prop_return_deepin_shm);
}
XImage *AppSnapshot::getImageXlib()
{
const auto display = QX11Info::display();
Window unused_window;
int unused_int;
unsigned unused_uint, w, h;
XGetGeometry(display, m_wid, &unused_window, &unused_int, &unused_int, &w, &h, &unused_uint, &unused_uint);
return XGetImage(display, m_wid, 0, 0, w, h, AllPlanes, ZPixmap);
}
QRect AppSnapshot::rectRemovedShadow(const QImage &qimage, unsigned char *prop_to_return_gtk)
{
const auto display = QX11Info::display();
const Atom gtk_frame_extents = XInternAtom(display, "_GTK_FRAME_EXTENTS", true);
Atom actual_type_return_gtk;
int actual_format_return_gtk;
unsigned long n_items_return_gtk;
unsigned long bytes_after_return_gtk;
const auto r = XGetWindowProperty(display, m_wid, gtk_frame_extents, 0, 4, false, XA_CARDINAL,
&actual_type_return_gtk, &actual_format_return_gtk, &n_items_return_gtk, &bytes_after_return_gtk, &prop_to_return_gtk);
if (!r && prop_to_return_gtk && n_items_return_gtk == 4 && actual_format_return_gtk == 32) {
qDebug() << "remove shadow frame...";
const unsigned long *extents = reinterpret_cast<const unsigned long *>(prop_to_return_gtk);
const int left = extents[0];
const int right = extents[1];
const int top = extents[2];
const int bottom = extents[3];
const int width = qimage.width();
const int height = qimage.height();
return QRect(left, top, width - left - right, height - top - bottom);
} else {
return QRect(0, 0, qimage.width(), qimage.height());
}
}
void AppSnapshot::getWindowState()
{
Atom actual_type;
int actual_format;
unsigned long i, num_items, bytes_after;
unsigned char *properties = nullptr;
m_isWidowHidden = false;
const auto display = QX11Info::display();
Atom atom_prop = XInternAtom(display, "_NET_WM_STATE", true);
if (!atom_prop) {
return;
}
Status status = XGetWindowProperty(display, m_wid, atom_prop, 0, LONG_MAX, False, AnyPropertyType, &actual_type, &actual_format, &num_items, &bytes_after, &properties);
if (status != Success) {
qDebug() << "Fail to get window state";
return;
}
Atom *atoms = reinterpret_cast<Atom *>(properties);
for(i = 0; i < num_items; ++i) {
const char *atomName = XGetAtomName(display, atoms[i]);
if (strcmp(atomName, "_NET_WM_STATE_HIDDEN") == 0) {
m_isWidowHidden = true;
break;
}
}
if (properties) {
XFree(properties);
}
}