-
Notifications
You must be signed in to change notification settings - Fork 35
/
Copy pathclipboardloader.cpp
326 lines (274 loc) · 9.77 KB
/
clipboardloader.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
// SPDX-FileCopyrightText: 2011 - 2022 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later
#include "clipboardloader.h"
#include <QApplication>
#include <QClipboard>
#include <QMimeData>
#include <QDir>
const QString PixCacheDir = "/.clipboard-pix"; // 图片缓存目录名
const int MAX_BETYARRAY_SIZE = 10*1024*1024; // 最大支持的文本大小
const int X11_PROTOCOL = 0; // x11协议
const int WAYLAND_PROTOCOL = 1; // wayland协议
QByteArray Info2Buf(const ItemInfo &info)
{
QByteArray buf;
QByteArray iconBuf;
if (info.m_formatMap.keys().contains("x-dfm-copied/file-icons")) {
iconBuf = info.m_formatMap["x-dfm-copied/file-icons"];
}
QDataStream stream(&buf, QIODevice::WriteOnly);
stream.setVersion(QDataStream::Qt_5_11);
stream << info.m_formatMap
<< info.m_type
<< info.m_urls
<< info.m_hasImage;
if (info.m_hasImage) {
stream << info.m_variantImage;
stream << info.m_pixSize;
}
stream << info.m_enable
<< info.m_text
<< info.m_createTime
<< iconBuf;
return buf;
}
ItemInfo Buf2Info(const QByteArray &buf)
{
QByteArray tempBuf = buf;
ItemInfo info;
QDataStream stream(&tempBuf, QIODevice::ReadOnly);
stream.setVersion(QDataStream::Qt_5_11);
int type;
QByteArray iconBuf;
stream >> info.m_formatMap
>> type
>> info.m_urls
>> info.m_hasImage;
if (info.m_hasImage) {
stream >> info.m_variantImage;
stream >> info.m_pixSize;
}
stream >> info.m_enable
>> info.m_text
>> info.m_createTime
>> iconBuf;
QDataStream stream2(&iconBuf, QIODevice::ReadOnly);
stream2.setVersion(QDataStream::Qt_5_11);
for (int i = 0 ; i < info.m_urls.size(); ++i) {
FileIconData data;
stream2 >> data.cornerIconList >> data.fileIcon;
info.m_iconDataList.push_back(data);
}
info.m_type = static_cast<DataType>(type);
return info;
}
QString ClipboardLoader::m_pixPath;
ClipboardLoader::ClipboardLoader(QObject *parent)
: QObject(parent)
, m_board(nullptr)
#ifdef USE_DEEPIN_KF5_WAYLAND
, m_waylandCopyClient(nullptr)
#endif
{
if (qEnvironmentVariable("XDG_SESSION_TYPE").contains("wayland")) {
#ifdef USE_DEEPIN_KF5_WAYLAND
m_waylandCopyClient = new WaylandCopyClient(this);
connect(m_waylandCopyClient, &WaylandCopyClient::dataChanged, this, [this] {
this->doWork(WAYLAND_PROTOCOL);
});
#else
qWarning() << "we will not work with wayland";
#endif
} else {
m_board = qApp->clipboard();
connect(m_board, &QClipboard::dataChanged, this, [this] {
this->doWork(X11_PROTOCOL);
});
}
QDir dir(QDir::homePath() + PixCacheDir);
if (dir.exists() && dir.removeRecursively()) {
qDebug() << "ClipboardLoader startup, remove old cache, path:" << dir.path();
}
}
void ClipboardLoader::dataReborned(const QByteArray &buf)
{
ItemInfo info;
info.m_variantImage = 0;
info = Buf2Info(buf);
QMimeData *mimeData = new QMimeData;
QMapIterator<QString, QByteArray> it(info.m_formatMap);
while (it.hasNext()) {
it.next();
mimeData->setData(it.key(), it.value());
}
switch (info.m_type) {
case DataType::Image:
setImageData(info, mimeData);
break;
default:
break;
}
if (m_board)
m_board->setMimeData(mimeData);
#ifdef USE_DEEPIN_KF5_WAYLAND
if (m_waylandCopyClient)
m_waylandCopyClient->setMimeData(mimeData);
#endif
}
void ClipboardLoader::doWork(int protocolType)
{
ItemInfo info;
info.m_variantImage = 0;
// 快速复制时mimedata很可能是无效的(一般表现为获取的数据为空), 下面是qt的说明
// The pointer returned might become invalidated when the contents
// of the clipboard changes; either by calling one of the setter functions
// or externally by the system clipboard changing.
#ifdef USE_DEEPIN_KF5_WAYLAND
const QMimeData *mimeData = protocolType == WAYLAND_PROTOCOL ? m_waylandCopyClient->mimeData() : m_board->mimeData();
#else
const QMimeData *mimeData = m_board->mimeData();
#endif
if (!mimeData || mimeData->formats().isEmpty())
return;
// 适配厂商云桌面粘贴问题
if (mimeData->formats().contains("uos/remote-copy")) {
qDebug() << "FROM_SHENXINFU_CLIPBOARD_MANAGER";
return;
}
// 转移系统剪贴板所有权时造成的两次内容变化不需要显示,以下为与系统约定好的标识
if (mimeData->data("FROM_DEEPIN_CLIPBOARD_MANAGER") == "1") {
qDebug() << "FROM_DEEPIN_CLIPBOARD_MANAGER";
return;
}
// 过滤重复数据
QByteArray currTimeStamp = mimeData->data("TIMESTAMP");
if (currTimeStamp == m_lastTimeStamp
&& m_lastTimeStamp != QByteArray::fromHex("00000000")// FIXME:TIM截图的时间戳不变,这里特殊处理
&& !currTimeStamp.isEmpty()) {// FIXME:连续双击两次图像,会异常到这里
qDebug() << "TIMESTAMP:" << currTimeStamp << m_lastTimeStamp;
return;
}
//图片类型的数据直接吧数据拿出来,不去调用mimeData->data()方法,会导致很卡
if (mimeData->hasImage()) {
const QPixmap &srcPix = qvariant_cast<QPixmap>(mimeData->imageData()) ;
if (srcPix.isNull())
return;
info.m_pixSize = srcPix.size();
if (!cachePixmap(srcPix, info)) {
info.m_variantImage = srcPix;
}
info.m_formatMap.insert(ApplicationXQtImageLiteral, info.m_variantImage.toByteArray());
info.m_formatMap.insert("TIMESTAMP", currTimeStamp);
if (info.m_variantImage.isNull())
return;
// 正常数据时间戳不为空,这里增加判断限制 时间戳为空+图片内容不变 重复数据不展示
// wayland下时间戳可能为空
if(currTimeStamp.isEmpty() && srcPix.toImage() == m_lastPix.toImage() && !qEnvironmentVariable("XDG_SESSION_TYPE").contains("wayland")) {
qDebug() << "system repeat image";
return;
}
m_lastPix = srcPix;
info.m_hasImage = true;
info.m_type = Image;
} else if (mimeData->hasUrls()) {
info.m_urls = mimeData->urls();
if (info.m_urls.isEmpty())
return;
//文件类型吧整个formats信息都拿出来,里面包含了文件的图标,以及文件的url数据等。
for (const QString &format : mimeData->formats()) {
const QByteArray &data = mimeData->data(format);
if (!data.isEmpty())
info.m_formatMap.insert(format, data);
}
if (info.m_formatMap.isEmpty())
return;
info.m_type = File;
} else {
if (mimeData->hasText()) {
info.m_text = mimeData->text();
} else if (mimeData->hasHtml()) {
info.m_text = mimeData->html();
} else {
return;
}
const QByteArray &textByteArray = info.m_text.toUtf8();
if (info.m_text.isEmpty() || textByteArray.size() > MAX_BETYARRAY_SIZE)
return;
info.m_formatMap.insert(TextPlainLiteral, std::move(textByteArray));
info.m_type = Text;
}
info.m_createTime = QDateTime::currentDateTime();
info.m_enable = true;
m_lastTimeStamp = currTimeStamp;
QByteArray buf;
buf = Info2Buf(info);
Q_EMIT dataComing(buf);
}
bool ClipboardLoader::cachePixmap(const QPixmap &srcPix, ItemInfo &info)
{
if (initPixPath()) {
QString pixFileName = m_pixPath + QString("/%1").arg(QDateTime::currentMSecsSinceEpoch());
QFile cacheFile(pixFileName);
if (!cacheFile.open(QIODevice::WriteOnly)) {
qDebug() << "open file failed, file name:" << pixFileName;
return false;
}
QDataStream stream(&cacheFile);
stream.setVersion(QDataStream::Qt_5_11);
stream << srcPix;
cacheFile.close();
info.m_variantImage = srcPix.width() * PixmapHeight > srcPix.height() * PixmapWidth ?
srcPix.scaledToWidth(PixmapWidth, Qt::SmoothTransformation) :
srcPix.scaledToHeight(PixmapHeight, Qt::SmoothTransformation);
info.m_urls.push_back(QUrl(pixFileName));
return true;
}
return false;
}
bool ClipboardLoader::initPixPath()
{
if (!m_pixPath.isEmpty()) {
return true;
}
QDir dir;
m_pixPath = QDir::homePath() + PixCacheDir;
if (dir.exists(m_pixPath)) {
qDebug() << "dir exists:" << m_pixPath;
return true;
}
if (dir.mkdir(m_pixPath)) {
qDebug() << "mkdir:" << m_pixPath;
return true;
}
qDebug() << "mkdir failed:" << m_pixPath;
m_pixPath.clear();
return false;
}
void ClipboardLoader::setImageData(const ItemInfo &info, QMimeData *&mimeData)
{
//正常处理的图片会有一个缓存url
if (info.m_urls.size() != 1) {
qDebug() << "url size error, size:" << info.m_urls.size();
mimeData->setImageData(info.m_variantImage);
return;
}
const QString &fileName = info.m_urls.front().path();
QFile cacheFile(fileName);
if (!cacheFile.open(QIODevice::ReadOnly)) {
qDebug() << "open pixmap cache file failed, file name:" << fileName;
mimeData->setImageData(info.m_variantImage);
return;
}
QPixmap pix;
QDataStream stream(&cacheFile);
stream.setVersion(QDataStream::Qt_5_11);
stream >> pix;
cacheFile.close();
if (pix.isNull()) {
qDebug() << "read pixmap cache file failed, file name:" << fileName;
mimeData->setImageData(info.m_variantImage);
return;
}
mimeData->setImageData(pix);
}